Purpose
- Build a weighted and directed network of genes based on the Dual RNA
seq data generated from the timecourse of Phytophthora cinnamomi and
Lupinus angustifolius
- Determine modules (clusters of genes) from the network associated to
disease progression (for example, biotrophic or necrotrophic
stages)
- Extract the names of the genes from these hubs and compare them to
the genes in the literature
- Functionally annotate these genes
Working idea: This network will then be used to identify hub genes
for each of the early, middle and late interactions.
Load Libraries
Load Phytophthora cinnamomi gtf file, clean up to get Gene IDs
Combine alternatively spliced transcripts (seen in transcript_id as
-RA and RB)
pc.gtf <- rtracklayer::import("https://ftp.ncbi.nlm.nih.gov/genomes/genbank/protozoa/Phytophthora_cinnamomi/latest_assembly_versions/GCA_018691715.1_ASM1869171v1/GCA_018691715.1_ASM1869171v1_genomic.gtf.gz")
trying URL 'https://ftp.ncbi.nlm.nih.gov/genomes/genbank/protozoa/Phytophthora_cinnamomi/latest_assembly_versions/GCA_018691715.1_ASM1869171v1/GCA_018691715.1_ASM1869171v1_genomic.gtf.gz'
Content type 'application/x-gzip' length 2722947 bytes (2.6 MB)
==================================================
downloaded 2.6 MB
pc.df <- as.data.frame((pc.gtf))
pc.genes <- filter(pc.df, type == "start_codon")
pc.geneids <- select(pc.genes, gene_id, product, protein_id)
pc.annotations <- pc.geneids %>%
distinct(gene_id, .keep_all = T)
nrow(pc.annotations)
[1] 19969
pcannot <- as.matrix(pc.annotations)
#write.table(pcannot, file = "pcgeneids.tsv",quote=FALSE,sep="\t")
Load GO terms blasted from Omicsbox
goterms <- read.delim('pc.goterms.txt', header = TRUE) %>%
select(c(SeqName, GO.IDs, GO.Names, Description))
head(goterms)
colnames(goterms) <- c("protein_id", "GO_terms", "GO_descripton", "description")
goterms.df <- dplyr::right_join(goterms, pc.annotations, by = 'protein_id')
nrow(goterms.df)
[1] 19969
Make GO term list for enrichment analysis
splitgt.df <- as.tibble(goterms.df) %>%
separate_longer_delim(c(GO_terms, GO_descripton), delim = ";")
Warning: `as.tibble()` was deprecated in tibble 2.0.0.
Please use `as_tibble()` instead.
The signature and semantics have changed, see `?as_tibble`.
splitgt.df <- as.data.frame(splitgt.df)
#strip leading spaces
splitgt.df$GO_terms <- gsub(" ","",splitgt.df$GO_terms)
splitgt.df$GO_descripton <- gsub("^ ","",splitgt.df$GO_descripton)
head(splitgt.df, 10)
gu <- unique(splitgt.df$GO_terms)
head(gu,20)
[1] "F:GO:0005515" "P:GO:0015074" "F:GO:0003676" "F:GO:0008270" "P:GO:0006355" "F:GO:0003700" "F:GO:0005509" "P:GO:0036297" "C:GO:0043240" "P:GO:0006629" "F:GO:0020037" "P:GO:0005975"
[13] "P:GO:0006508" "F:GO:0030248" "C:GO:0005576" "P:GO:0006801" "F:GO:0046872" "F:GO:0005525" "P:GO:0007076" "C:GO:0016020"
gul <- lapply(1:length(gu), function(i){
mygo <- gu[i]
unique(splitgt.df[splitgt.df$GO_terms == mygo, "gene_id"])
})
names(gul) <- lapply(1:length(gu), function(i){
mygo <- gu[i]
desc <- head(splitgt.df[splitgt.df$GO_terms == mygo, "GO_descripton"],1)
id_desc <- paste(mygo,desc)
})
head(names(gul))
[1] "F:GO:0005515 F:protein binding" "P:GO:0015074 P:DNA integration" "F:GO:0003676 F:nucleic acid binding"
[4] "F:GO:0008270 F:zinc ion binding" "P:GO:0006355 P:regulation of DNA-templated transcription" "F:GO:0003700 F:DNA-binding transcription factor activity"
Assemble count matrix and coldata file
Import files into r
coldata <- read.table('sample_info.tsv')
Pcgenenames <- read.table('pcgeneids.tsv', row.names = 1, quote = "", sep='\t', fill = TRUE, header = TRUE)
IUM83Tanjilcountmatrix <- read.table('countmatrix.tsv')
Clean countmatrix for P. cinnamomi analysis
collabels <- colnames(IUM83Tanjilcountmatrix) <- (coldata$Sample)
colnames(IUM83Tanjilcountmatrix) <- collabels
IUM83Tanjilcountmatrix <- tibble::rownames_to_column(IUM83Tanjilcountmatrix, "gene_id")
IUM83CountMatrix <- IUM83Tanjilcountmatrix %>%
filter(str_detect(gene_id, "IUM83"))
rownames(IUM83CountMatrix) <- IUM83CountMatrix$gene_id
IUM83CountMatrix <- IUM83CountMatrix [ ,-1]
IUM83CountMatrix <- as.data.frame(IUM83CountMatrix)
IUM83CountMatrix <- IUM83CountMatrix[ ,-(1:12)]
#write.table(IUM83CountMatrix, file = "Pc_countmatrix.tsv",quote=FALSE,sep="\t")
Clean coldata for Deseq2 normalisation
colData <- coldata %>%
filter(str_detect(Sample, "Pc"))
rownames(colData) <- colData$Sample
colData <- colData [ ,-1]
Detect and remove outlier genes
#Call a function from WGCNA package that detects outliers
#Rows should be the samples and the columns genes
gsg <- goodSamplesGenes(t(IUM83CountMatrix))
Flagging genes and samples with too many missing values...
..step 1
..step 2
summary(gsg)
Length Class Mode
goodGenes 19981 -none- logical
goodSamples 12 -none- logical
allOK 1 -none- logical
#If false, then there are outliers
gsg$allOK #False
[1] FALSE
table(gsg$goodGenes) #3012to be excluded
FALSE TRUE
3012 16969
table(gsg$goodSamples) #All 12 samples passed
TRUE
12
# remove genes that are detectd as outliers
data <- IUM83CountMatrix[gsg$goodGenes == TRUE,]
Detect outlier samples
# detect outlier samples - hierarchical clustering
htree <- hclust(dist(t(data)), method = "average")
groups <- cutree(htree, k=2) # cut tree into clusters
plot(htree, labels(groups))
# draw dendogram with red borders around the clusters
rect.hclust(htree, k=2, border="red")

Remove outlier samples
clusters <- as.data.frame(groups)
head(clusters)
table(clusters)
groups
1 2
3 9
# the biggest cluster is cluster 2 and the rest will be tagged as outliers
outliers <- clusters %>% filter(., !groups == 2) %>% row.names()
# exclude outlier samples
data.subset <- data[,!(colnames(data) %in% outliers)]
Normalisation using DESeq2 package
The WGCNA library requires normalisation using the vst
(variance-stabilising transform) function from the DESeq2 package
# exclude outlier samples from the column data to match the new data
colData <- colData %>%
filter(!row.names(.) %in% outliers)
# making sure the rownames and column names identical for the two data sets
all(rownames(colData) == colnames(data.subset))
[1] TRUE
# create DESeq Data Set
dds <- DESeqDataSetFromMatrix(countData = data.subset,
colData = colData,
design = ~ 1) # no model, for normalisation only
# remove all genes with counts < 10 in more than 90% of samples (12*0.9=10.8 ~ 11) as suggested by WGCNA on RNAseq FAQ (https://horvath.genetics.ucla.edu/html/CoexpressionNetwork/Rpackages/WGCNA/faq.html)
# <10 in more than 90% samples
dds90 <- dds[rowSums(counts(dds) >= 10) >= 8,]
nrow(dds90) # 5403 genes
[1] 5541
# >= 15) >= 8,] 4538 genes
# >= 10) >= 8,] 5541 genes <===== #less than 10 counts in %90 of samples
gene.list <- row.names(dds90)
#lapply(gene.list, write, "all_genes.txt", append=TRUE)
# perform variance stabilization
dds_norm <- vst(dds90)
# get normalized counts
norm.counts <- assay(dds_norm) %>%
t()
#Save normalised dataset for faster analysis next time
saveRDS(norm.counts, "norm.counts.rds")
Network Construction
# load norm.counts object
#norm.counts <- readRDS("norm.counts")
# Choose a set of soft-thresholding powers to create a scale-free network
power <- c(c(1:10), seq(from = 12, to = 30, by = 2))
# Call the network topology analysis function
sft <- pickSoftThreshold(norm.counts,
powerVector = power,
networkType = "signed",
RsquaredCut = 0.8,
verbose = 5)
pickSoftThreshold: will use block size 5541.
pickSoftThreshold: calculating connectivity for given powers...
..working on genes 1 through 5541 of 5541
Warning: executing %dopar% sequentially: no parallel backend registered
sft.data <- sft$fitIndices
a1 <- ggplot(sft.data, aes(Power, SFT.R.sq, label = Power)) +
geom_point() +
geom_text(nudge_y = 0.1) +
geom_hline(yintercept = 0.80, color = 'red') +
labs(x = 'Power',
y = 'Scale free topology model fit, signed R^2',
title = 'Scale independence') +
theme_classic()
a2 <- ggplot(sft.data, aes(Power, mean.k., label = Power)) +
geom_point() +
geom_text(nudge_y = 0.1) +
labs(x = 'Power',
y = 'Mean Connectivity',
title = 'Mean connectivity') +
theme_classic()
grid.arrange(a1, a2, nrow = 2)

soft_power <- sft$powerEstimate ### 16
#soft_power <- 16
Create network and identify modules bwnet <-
blockwiseModules(norm.counts, maxBlockSize = 30000, # selected total
number of genes since CPU memory is sufficient (32GB workstation should
be able to handle perhaps 30000) TOMType = “signed”, power = soft_power,
mergeCutHeight = 0.25, numericLabels = FALSE, # set as false to assign
color as labels randomSeed = 1234, # for reproducibility since this
function uses clustering verbose = 3) Note: this chunk is set not to run
since the blockwise Modules function takes a long time to run. The R
object has been saved and is loaded in the next chunk for a faster run
time.
# load bwnet object
bwnet <- readRDS("bwnet.rds")
module_eigengenes <- bwnet$MEs %>%
orderMEs(.)
head(module_eigengenes)
# Plot the dendrogram and the module colors before and after merging underneath
plotDendroAndColors(bwnet$dendrograms[[1]], cbind(bwnet$unmergedColors, bwnet$colors),
c("unmerged", "merged"),
dendroLabels = FALSE,
addGuide = TRUE,
hang= 0.03,
guideHang = 0.05)

# grey module = all genes that doesn't fall into other modules
module.total <- table(bwnet$colors)
Relate the modules to stages
# create traits file - assign 1 if a sample is a certain stage, else assign 0
#Dis_traits <- colData %>%
# mutate(Dis.vs.all = ifelse(grepl('Treated', Treatment), 1, 0)) %>%
# select(4)
factor_levels <- unique(colData$Stage)
# transform stages into factors and define levels
colData$Stage <- factor(colData$Stage, levels = factor_levels)
traits <- binarizeCategoricalColumns(colData$Stage,
dropFirstLevelVsAll = FALSE,
includePairwise = FALSE,
includeLevelVsAll = TRUE,
minCount = 1)
rownames(traits) <- colData$Sample
Visualise module-trait association as a heatmap

Significance in brackets shows how significantly associated the
module (cluster of genes) is the trait of interest
Find modules that have significant association with disease state
Calculate the module membership and the associated p-values The
module membership/intramodular connectivity is calculated as the
correlation of the eigengene and the gene expression profile. This
quantifies the similarity of all genes on the array to every module.
Using the gene significance you can identify genes that have a high
significance for trait of interest Using the module membership measures
you can identify genes with high module membership in interesting
modules.
Gene Significance at the Early Pc-lupin interaction
# Define a gene significance variable for Early
GS.Early <- as.numeric(cor(norm.counts, traits$data.Early.vs.all, use = "p"))
# This translates the numeric values into colors
GS.stage_earlyColor = numbers2colors(GS.Early, signed = T)
blocknumber = 1
moduleLabelsAutomatic = bwnet$colors
# Convert labels to colors for plotting
moduleColorsAutomatic = labels2colors(moduleLabelsAutomatic)
datColors = data.frame(bwnet$colors, GS.stage_earlyColor)[bwnet$blockGenes[[blocknumber]],
]
# Plot the dendrogram and the module colors underneath
plotDendroAndColors(bwnet$dendrograms[[blocknumber]], colors = datColors, groupLabels = c("Module colors",
"GS.stage.Early"), dendroLabels = FALSE, hang = 0.03, addGuide = TRUE, guideHang = 0.05)

datKME <- signedKME(norm.counts, module_eigengenes)
# Measure the correlation of the gene's module membership and the associated p-values
module.membership.measure <- cor(module_eigengenes, norm.counts, use = 'p')
module.membership.measure.pvals <- corPvalueStudent(module.membership.measure, nSamples)
# Calculate the gene significance and associated p-values
early.gene.signf.corr <- cor(norm.counts, traits$data.Early.vs.all, use = 'p')
early.gene.signf.corr.pvals <- corPvalueStudent(early.gene.signf.corr, nSamples)
colorOfColumn = substring(names(datKME), 4)
#selectModules = c("brown", "magenta", "blue", "turquoise", "lightcyan")
selectModules = colorOfColumn[colorOfColumn !="grey"]
#par(mfrow = c(4, length(selectModules)/4))
for (module in selectModules) {
column = match(module, colorOfColumn)
restModule = moduleColorsAutomatic == module
verboseScatterplot(datKME[restModule, column], GS.Early[restModule], xlab = paste("Module Membership",
module, "module"), ylab = "GS.stage_Early", main = paste("kME.", module,
"vs. GS"), col = module)
}

















earlygs <- early.gene.signf.corr %>%
as.data.frame() %>%
tibble::rownames_to_column("gene_id")
earlygsid <- dplyr::right_join(pc.annotations, earlygs, by = 'gene_id')
datKme.id <- as.data.frame(datKME) %>%
rownames_to_column("gene_id")
earlyeiginengene <- dplyr::right_join(earlygsid, datKme.id, by = 'gene_id')
Gene Significance at the Mid Pc-lupin interaction
earlygs <- early.gene.signf.corr %>%
as.data.frame() %>%
tibble::rownames_to_column("gene_id")
earlygsid <- dplyr::right_join(pc.annotations, earlygs, by = 'gene_id')
datKme.id <- as.data.frame(datKME) %>%
rownames_to_column("gene_id")
earlyeiginengene <- dplyr::right_join(earlygsid, datKme.id, by = 'gene_id')
# Define a gene significance variable for Early
GS.mid <- as.numeric(cor(norm.counts, traits$data.Middle.vs.all, use = "p"))
# This translates the numeric values into colors
GS.stage_midColor = numbers2colors(GS.mid, signed = T)
blocknumber = 1
moduleLabelsAutomatic = bwnet$colors
# Convert labels to colors for plotting
moduleColorsAutomatic = labels2colors(moduleLabelsAutomatic)
datColors = data.frame(bwnet$colors, GS.stage_midColor)[bwnet$blockGenes[[blocknumber]],
]
# Plot the dendrogram and the module colors underneath
plotDendroAndColors(bwnet$dendrograms[[blocknumber]], colors = datColors, groupLabels = c("Module colors",
"GS.mid"), dendroLabels = FALSE, hang = 0.03, addGuide = TRUE, guideHang = 0.05)

datKME <- signedKME(norm.counts, module_eigengenes)
# Measure the correlation of the gene's module membership and the associated p-values
module.membership.measure <- cor(module_eigengenes, norm.counts, use = 'p')
module.membership.measure.pvals <- corPvalueStudent(module.membership.measure, nSamples)
# Calculate the gene significance and associated p-values
mid.gene.signf.corr <- cor(norm.counts, traits$data.Middle.vs.all, use = 'p')
mid.gene.signf.corr.pvals <- corPvalueStudent(mid.gene.signf.corr, nSamples)
colorOfColumn = substring(names(datKME), 4)
#selectModules = c("red", "tan", "salmon","green", "magenta")
selectModules = colorOfColumn[colorOfColumn !="grey"]
#par(mfrow = c(3, length(selectModules)/3))
for (module in selectModules) {
column = match(module, colorOfColumn)
restModule = moduleColorsAutomatic == module
verboseScatterplot(datKME[restModule, column], GS.mid[restModule], xlab = paste("Module Membership",
module, "module"), ylab = "GS.stage_mid", main = paste("kME.", module,
"vs. GS"), col = module)
}

















Gene Significance at the Late Pc-lupin interaction
midgs <- mid.gene.signf.corr %>%
as.data.frame() %>%
tibble::rownames_to_column("gene_id")
midgsid <- dplyr::right_join(Pcgenenames, midgs, by = 'gene_id')
datKme.id <- as.data.frame(datKME) %>%
rownames_to_column("gene_id")
mideiginengene <- dplyr::right_join(midgsid, datKme.id, by = 'gene_id')
# Define a gene significance variable for Early
GS.late <- as.numeric(cor(norm.counts, traits$data.Late.vs.all, use = "p"))
# This translates the numeric values into colors
GS.stage_lateColor = numbers2colors(GS.late, signed = T)
blocknumber = 1
moduleLabelsAutomatic = bwnet$colors
# Convert labels to colors for plotting
moduleColorsAutomatic = labels2colors(moduleLabelsAutomatic)
datColors = data.frame(bwnet$colors, GS.stage_lateColor)[bwnet$blockGenes[[blocknumber]],
]
# Plot the dendrogram and the module colors underneath
plotDendroAndColors(bwnet$dendrograms[[blocknumber]], colors = datColors, groupLabels = c("Module colors",
"GS.stage.Late"), dendroLabels = FALSE, hang = 0.03, addGuide = TRUE, guideHang = 0.05)

turquoise module (Horvath cares not for the reported cor=). Instead,
Look at the y-axis: genes that have high positive module membership tend
to be highly positively correlated with the late interaction of Pc in
lupin, whereas, genes with negative values in the module they have
negative relations with the late interaction of Pc in lupin. Can select
either; genes with high positive/negative GS or high kMe (hub genes)
datKME <- signedKME(norm.counts, module_eigengenes)
# Measure the correlation of the gene's module membership and the associated p-values
module.membership.measure <- cor(module_eigengenes, norm.counts, use = 'p')
module.membership.measure.pvals <- corPvalueStudent(module.membership.measure, nSamples)
# Calculate the gene significance and associated p-values
late.gene.signf.corr <- cor(norm.counts, traits$data.Late.vs.all, use = 'p')
late.gene.signf.corr.pvals <- corPvalueStudent(late.gene.signf.corr, nSamples)
colorOfColumn = substring(names(datKME), 4)
#selectModules = c("green", "turquoise", "blue", "grey60", "salmon")
selectModules = colorOfColumn[colorOfColumn !="grey"]
#par(mfrow = c(3, length(selectModules)/3))
for (module in selectModules) {
column = match(module, colorOfColumn)
restModule = moduleColorsAutomatic == module
verboseScatterplot(datKME[restModule, column], GS.late[restModule], xlab = paste("Module Membership",
module, "module"), ylab = "GS.stage_Late", main = paste("kME.", module,
"vs. GS"), col = module)
}

















lategs <- late.gene.signf.corr.pvals %>%
as.data.frame() %>%
tibble::rownames_to_column("gene_id")
lategsid <- dplyr::right_join(Pcgenenames, lategs, by = 'gene_id')
datKme.id <- as.data.frame(datKME) %>%
rownames_to_column("gene_id")
lateeiginengene <- dplyr::right_join(lategsid, datKme.id, by = 'gene_id')
# Heatmap of old module eigen-genes and samples
#pdf(file="oldMEs.pdf",heigh=80,width=20)
library("pheatmap")
MEs <- bwnet$MEs
rownames(MEs)=names(norm.counts[,9])
pheatmap(MEs,cluster_col=T,cluster_row=T,show_rownames=F,show_colnames=T,fontsize=6)
# Heatmap of new module eigen-genes and sample trait (e.g. Zone)
col_ann <- colData[,c(1,4)]
rownames(col_ann) <- col_ann$Sample
col_ann <- data.frame(col_ann)
col_ann$Stage <- as.factor(col_ann$Stage)
col_ann <- col_ann[order(col_ann$Stage),]
col_ann$sample_ID <- NULL
head(col_ann, 9)
ann_color <- list("col_ann" = c("Early" = "yellow",
"Middle" = "green",
"Late" = "blue"))
data.me <- data.frame(MEs)
data.me <- data.me[order(match(rownames(data.me), rownames(col_ann))),]
dim(data.me)
#pdf(file="newMEs.pdf",heigh=60,width=20)
rownames(MEs)=names(colData[ ,1])
pheatmap(data.me,cluster_col=T,cluster_row=F,show_rownames=F,
show_colnames=T,fontsize=6,
annotation_row = col_ann, annotation_colors = ann_color)
library("clusterProfiler")
library("kableExtra")
writeGMT <- function (object, fname ){
if (class(object) != "list") stop("object should be of class 'list'")
if(file.exists(fname)) unlink(fname)
for (iElement in 1:length(object)){
write.table(t(c(make.names(rep(names(object)[iElement],2)),object[[iElement]])),
sep="\t",quote=FALSE,
file=fname,append=TRUE,col.names=FALSE,row.names=FALSE)
}
}
writeGMT(object=gul,fname="goterms.gmt")
genesets <- read.gmt("goterms.gmt")
Run clusterprofiler to do enrichment of GOred with gul gene sets.
bg <- gene.list
head(bg)
head(genesets)
#early
#green <- subset(earlyeiginengene, V1 > 0.2 & earlyeiginengene$kMEgreen> 0.7)
#gogreen <- green$gene_id
#cyan <- subset(earlyeiginengene, V1 > 0.7 & earlyeiginengene$kMEcyan> 0.7)
#blue <- subset(earlyeiginengene, V1 > 0.7 & earlyeiginengene$kMEblue> 0.7)
#magenta <- subset(earlyeiginengene, V1 > 0.7 & earlyeiginengene$kMEmagenta > 0.7)
#lightcyan <- subset(earlyeiginengene, V1 > 0.7 & earlyeiginengene$kMElightcyan > 0.7)
#gocyan <- cyan$gene_id
#goblue <- blue$gene_id
#gomagenta <- magenta$gene_id
#lightcyango <- lightcyan$gene_id
#mid
#red <- subset(mideiginengene, V1 > 0.2 & mideiginengene$kMEred > 0.7)
#gored <- red$gene_id
#tan <- subset(mideiginengene, V1 > 0.2 & mideiginengene$kMEtan> 0.7)
#gotan <- tan$gene_id
#green <- subset(mideiginengene, V1 > 0.2 & mideiginengene$kMEgreen> 0.7)
#gogreen <- green$gene_id
#late
#turquoise <- subset(lateeiginengene, V1 > 0.7 & lateeiginengene$kMEturquoise > 0.7)
#goturquoise <- turquoise$gene_id
#brown <- subset(lateeiginengene, V1 > 0.5 & lateeiginengene$kMEbrown > 0.5)
#gobrown <- brown$gene_id
#midnightblue <- subset(lateeiginengene, V1 > 0.5 & lateeiginengene$kMEmidnightblue > 0.5)
#gomidnightblue <- midnightblue$gene_id
#lightcyan
#magenta
yellow <- subset(lateeiginengene, V1 > 0.2 & lateeiginengene$kMEyellow > 0.7)
goyellow <- yellow$gene_id
ora_res <- as.data.frame(enricher(gene = goyellow ,
universe = bg, maxGSSize = 5000, TERM2GENE = genesets,
pAdjustMethod="fdr", pvalueCutoff = 1, qvalueCutoff = 1 ))
ora_res$geneID <- NULL
ora_res <- subset(ora_res,p.adjust<0.05 & Count >=5)
ora_res_names <- rownames(ora_res)
head(ora_res)
gr <- as.numeric(sapply(strsplit(ora_res$GeneRatio,"/"),"[[",1)) /
as.numeric(sapply(strsplit(ora_res$GeneRatio,"/"),"[[",2))
br <- as.numeric(sapply(strsplit(ora_res$BgRatio,"/"),"[[",1)) /
as.numeric(sapply(strsplit(ora_res$BgRatio,"/"),"[[",2))
ora_res$es <- gr/br
ora_res <- ora_res[order(-ora_res$es),]
ora_res$Description=NULL
head(ora_res) %>%
kbl(row.names = FALSE, caption="Top upregulated pathways in cluster") %>%
kable_paper("hover", full_width = F)
topup2 <- rev(head(ora_res$es,10))
names(topup2) <- rev(head(ora_res$ID,10))
head(topup2)
par(mar=c(5,20,5,1))
barplot(c(topup2),
horiz=TRUE,las=1,cex.names=0.65,
main="top GO enrichments",
xlab="ES",
cex.main=0.9)
mtext("ORA test")
Run clusterprofiler to do enrichment of GOred with gul gene sets.
#```{r,clusterprofiler2}
bg <- gene.list
ora_res <- as.data.frame(enricher(gene = gogreen , universe = bg,
maxGSSize = 5000, TERM2GENE = genesets, pAdjustMethod=“fdr”,
pvalueCutoff = 1, qvalueCutoff = 1 ))
ora_res$geneID <- NULL ora_res <-
subset(ora_res,p.adjust<0.05 & Count >=5) ora_res_names <-
rownames(ora_res)
head(ora_res)
gr <- as.numeric(sapply(strsplit(ora_res\(GeneRatio,"/"),"[[",1))
/ as.numeric(sapply(strsplit(ora_res\)GeneRatio,“/”),“[[”,2))
br <- as.numeric(sapply(strsplit(ora_res\(BgRatio,"/"),"[[",1))
/ as.numeric(sapply(strsplit(ora_res\)BgRatio,“/”),“[[”,2))
ora_res\(es <- gr/br ora_res <-
ora_res[order(-ora_res\)es),] ora_res$Description=NULL
head(ora_res) %>% kbl(row.names = FALSE, caption=“Top upregulated
pathways in cluster”) %>% kable_paper(“hover”, full_width = F)
topup2 <- rev(head(ora_res\(es,10))
names(topup2) <- rev(head(ora_res\)ID,10))
head(topup2)
par(mar=c(5,20,5,1))
barplot(c(topup2), horiz=TRUE,las=1,cex.names=0.65, main=“top GO
enrichments”, xlab=“ES”, cex.main=0.9)
mtext(“ORA test”)
#```
#```{r} module_df <- data.frame( gene_id = names(bwnet\(colors), colors =
labels2colors(bwnet\)colors) )
modules.of.interest <- c(“red”, “tan”, “green”, “black”)
submod <- module_df %>% subset(colors %in%
modules.of.interest)
row.names(module_df) = module_df$gene_id
subexpr = norm.counts %>% t()
subexpr = subexpr[submod$gene_id,]
submod_df = data.frame(subexpr) %>% mutate( gene_id = row.names(.)
) %>% pivot_longer(-gene_id) %>% mutate( module =
module_df[gene_id,]$colors )
submod_df %>% ggplot(., aes(x=name, y=value, group=gene_id)) +
geom_line(aes(color = module), alpha = 0.2) + theme_bw() + theme(
axis.text.x = element_text(angle = 90) ) + facet_grid(rows =
vars(module)) + labs(x = “treatment”, y = “normalized expression”)
#```
Export to Cytoscape
#```{r} load(“pc_TOM-block.1.RData”)
TOM.mat = as.matrix(TOM) # choose module module = “cyan” # get list
of genes probes = names(as.data.frame(norm.counts))
moduleColors = labels2colors(bwnet$colors)
inModule <- (moduleColors==module) modProbes = probes[inModule] #
Select the corresponding Topological Overlap modTOM = TOM.mat[inModule,
inModule]
Export the network into edge and node list files for Cytoscape
cyt = exportNetworkToCytoscape(modTOM,
edgeFile=paste(“CytoEdge”,paste(module,collapse=“-”),“.txt”,sep=““),
nodeFile=paste(”CytoNode”,paste(module,collapse=“-”),“.txt”,sep=““),
weighted = TRUE, threshold = 0.02,nodeNames=modProbes, nodeAttr =
moduleColors[inModule]) #```
#```{r} edge.annotation <- as.data.frame(apply(pc.annotations, #
Remove spaces within protein ID 2, function(x) gsub(“\s+”, ““, x)))
edge.annotation
edge <- read.delim(“CytoEdgelightgreen.txt”) colnames(edge)
colnames(edge) <- c(“source”,
“target”,“weight”,“direction”,“fromAltName”,“toAltName”)
node <- read.delim(“CytoNodelightgreen.txt”) colnames(node)
colnames(node) <- c(“gene_id”,“altName”,“node_attributes”) nodeID
<- dplyr::right_join(edge.annotation, node, by = ‘gene_id’)
nodeID
write.csv(nodeID, file = ‘lightgreennode.txt’) #```
Session Information
LS0tCnRpdGxlOiAiV0dDTkEgb2YgUGMgdGltZWNvdXJzZSIKYXV0aG9yOiAiQlNjaHJvZXRlciIKZGF0ZTogIjIwMjMtMDMtMjAiCm91dHB1dDoKICBodG1sX2RvY3VtZW50OgogICAgdG9jOiB5ZXMKICAgIGRmX3ByaW50OiBwYWdlZAogIHByZXR0eWRvYzo6aHRtbF9wcmV0dHk6CiAgICB0aGVtZTogaHBzdHIKICAgIGhpZ2hsaWdodDogZ2l0aHViCiAgaHRtbF9ub3RlYm9vazoKICAgIHRoZW1lOiBsdW1lbgogICAgdG9jOiB5ZXMKICAgIHRvY19mbG9hdDogeWVzCi0tLQoKIyBQdXJwb3NlCgoxLiBCdWlsZCBhIHdlaWdodGVkIGFuZCBkaXJlY3RlZCBuZXR3b3JrIG9mIGdlbmVzIGJhc2VkIG9uIHRoZSBEdWFsIFJOQSBzZXEgZGF0YSBnZW5lcmF0ZWQgZnJvbSB0aGUgdGltZWNvdXJzZSBvZiBQaHl0b3BodGhvcmEgY2lubmFtb21pIGFuZCBMdXBpbnVzIGFuZ3VzdGlmb2xpdXMgCjIuIERldGVybWluZSBtb2R1bGVzIChjbHVzdGVycyBvZiBnZW5lcykgZnJvbSB0aGUgbmV0d29yayBhc3NvY2lhdGVkIHRvIGRpc2Vhc2UgcHJvZ3Jlc3Npb24gKGZvciBleGFtcGxlLCBiaW90cm9waGljIG9yIG5lY3JvdHJvcGhpYyBzdGFnZXMpIAozLiBFeHRyYWN0IHRoZSBuYW1lcyBvZiB0aGUgZ2VuZXMgZnJvbSB0aGVzZSBodWJzIGFuZCBjb21wYXJlIHRoZW0gdG8gdGhlIGdlbmVzIGluIHRoZSBsaXRlcmF0dXJlIAo0LiBGdW5jdGlvbmFsbHkgYW5ub3RhdGUgdGhlc2UgZ2VuZXMKCldvcmtpbmcgaWRlYTogVGhpcyBuZXR3b3JrIHdpbGwgdGhlbiBiZSB1c2VkIHRvIGlkZW50aWZ5IGh1YiBnZW5lcyBmb3IgZWFjaCBvZiB0aGUgZWFybHksIG1pZGRsZSBhbmQgbGF0ZSBpbnRlcmFjdGlvbnMuCgojIyBMb2FkIExpYnJhcmllcwoKYGBge3IgbWVzc2FnZT1GQUxTRSwgd2FybmluZz1GQUxTRSwgaW5jbHVkZT1GQUxTRSwgcGFnZWQucHJpbnQ9VFJVRX0Kc3VwcHJlc3NQYWNrYWdlU3RhcnR1cE1lc3NhZ2VzKHtjKApsaWJyYXJ5KFdHQ05BKSwKbGlicmFyeShERVNlcTIpLApsaWJyYXJ5KEdFT3F1ZXJ5KSwKbGlicmFyeSh0aWR5dmVyc2UpLApsaWJyYXJ5KGdyaWRFeHRyYSksCmxpYnJhcnkocmVzaGFwZTIpLApsaWJyYXJ5KHJ0cmFja2xheWVyKSwKbGlicmFyeShkcGx5cikpfSkKQmlvY01hbmFnZXI6Omluc3RhbGwoJ2thYmxlRXh0cmEnKQpgYGAKCkxvYWQgUGh5dG9waHRob3JhIGNpbm5hbW9taSBndGYgZmlsZSwgY2xlYW4gdXAgdG8gZ2V0IEdlbmUgSURzCgpDb21iaW5lIGFsdGVybmF0aXZlbHkgc3BsaWNlZCB0cmFuc2NyaXB0cyAoc2VlbiBpbiB0cmFuc2NyaXB0X2lkIGFzIC1SQSBhbmQgUkIpCgpgYGB7cn0KcGMuZ3RmIDwtIHJ0cmFja2xheWVyOjppbXBvcnQoImh0dHBzOi8vZnRwLm5jYmkubmxtLm5paC5nb3YvZ2Vub21lcy9nZW5iYW5rL3Byb3Rvem9hL1BoeXRvcGh0aG9yYV9jaW5uYW1vbWkvbGF0ZXN0X2Fzc2VtYmx5X3ZlcnNpb25zL0dDQV8wMTg2OTE3MTUuMV9BU00xODY5MTcxdjEvR0NBXzAxODY5MTcxNS4xX0FTTTE4NjkxNzF2MV9nZW5vbWljLmd0Zi5neiIpCnBjLmRmIDwtIGFzLmRhdGEuZnJhbWUoKHBjLmd0ZikpCgpwYy5nZW5lcyA8LSBmaWx0ZXIocGMuZGYsIHR5cGUgPT0gInN0YXJ0X2NvZG9uIikKcGMuZ2VuZWlkcyA8LSBzZWxlY3QocGMuZ2VuZXMsIGdlbmVfaWQsIHByb2R1Y3QsIHByb3RlaW5faWQpICAKCnBjLmFubm90YXRpb25zIDwtIHBjLmdlbmVpZHMgJT4lIAogIGRpc3RpbmN0KGdlbmVfaWQsIC5rZWVwX2FsbCA9IFQpCm5yb3cocGMuYW5ub3RhdGlvbnMpCgpwY2Fubm90IDwtIGFzLm1hdHJpeChwYy5hbm5vdGF0aW9ucykKCiN3cml0ZS50YWJsZShwY2Fubm90LCBmaWxlID0gInBjZ2VuZWlkcy50c3YiLHF1b3RlPUZBTFNFLHNlcD0iXHQiKQpgYGAKCkxvYWQgR08gdGVybXMgYmxhc3RlZCBmcm9tIE9taWNzYm94IApgYGB7cn0KZ290ZXJtcyA8LSByZWFkLmRlbGltKCdwYy5nb3Rlcm1zLnR4dCcsIGhlYWRlciA9IFRSVUUpICU+JQogIHNlbGVjdChjKFNlcU5hbWUsIEdPLklEcywgR08uTmFtZXMsIERlc2NyaXB0aW9uKSkKICAKaGVhZChnb3Rlcm1zKQoKY29sbmFtZXMoZ290ZXJtcykgPC0gYygicHJvdGVpbl9pZCIsICJHT190ZXJtcyIsICJHT19kZXNjcmlwdG9uIiwgImRlc2NyaXB0aW9uIikKCmdvdGVybXMuZGYgPC0gZHBseXI6OnJpZ2h0X2pvaW4oZ290ZXJtcywgcGMuYW5ub3RhdGlvbnMsIGJ5ID0gJ3Byb3RlaW5faWQnKQpucm93KGdvdGVybXMuZGYpCmBgYAoKTWFrZSBHTyB0ZXJtIGxpc3QgZm9yIGVucmljaG1lbnQgYW5hbHlzaXMKCmBgYHtyfQpzcGxpdGd0LmRmIDwtIGFzLnRpYmJsZShnb3Rlcm1zLmRmKSAlPiUKICBzZXBhcmF0ZV9sb25nZXJfZGVsaW0oYyhHT190ZXJtcywgR09fZGVzY3JpcHRvbiksIGRlbGltID0gIjsiKQoKc3BsaXRndC5kZiA8LSBhcy5kYXRhLmZyYW1lKHNwbGl0Z3QuZGYpCgojc3RyaXAgbGVhZGluZyBzcGFjZXMKc3BsaXRndC5kZiRHT190ZXJtcyA8LSBnc3ViKCIgIiwiIixzcGxpdGd0LmRmJEdPX3Rlcm1zKQpzcGxpdGd0LmRmJEdPX2Rlc2NyaXB0b24gPC0gZ3N1YigiXiAiLCIiLHNwbGl0Z3QuZGYkR09fZGVzY3JpcHRvbikKaGVhZChzcGxpdGd0LmRmLCAxMCkKCmd1IDwtIHVuaXF1ZShzcGxpdGd0LmRmJEdPX3Rlcm1zKQpoZWFkKGd1LDIwKQoKZ3VsIDwtIGxhcHBseSgxOmxlbmd0aChndSksIGZ1bmN0aW9uKGkpewogIG15Z28gPC0gZ3VbaV0KICB1bmlxdWUoc3BsaXRndC5kZltzcGxpdGd0LmRmJEdPX3Rlcm1zID09IG15Z28sICJnZW5lX2lkIl0pCn0pCgpuYW1lcyhndWwpIDwtIGxhcHBseSgxOmxlbmd0aChndSksIGZ1bmN0aW9uKGkpewogIG15Z28gPC0gZ3VbaV0KICBkZXNjIDwtIGhlYWQoc3BsaXRndC5kZltzcGxpdGd0LmRmJEdPX3Rlcm1zID09IG15Z28sICJHT19kZXNjcmlwdG9uIl0sMSkKICBpZF9kZXNjIDwtIHBhc3RlKG15Z28sZGVzYykKfSkKCmhlYWQobmFtZXMoZ3VsKSkKCmBgYAoKQXNzZW1ibGUgY291bnQgbWF0cml4IGFuZCBjb2xkYXRhIGZpbGUKCmBgYHtyLCBlY2hvPUZBTFNFfQp0bXA8LXJlYWQudGFibGUoIjNjb2wudHN2Lmd6IixoZWFkZXI9RikKeDwtYXMubWF0cml4KGFjYXN0KHRtcCwgVjJ+VjEsIHZhbHVlLnZhcj0iVjMiKSkKY29sbmFtZXMoeCk8LXNhcHBseShzdHJzcGxpdChjb2xuYW1lcyh4KSwiXyIpLCJbWyIsMSkKCiNoZWFkKHgpCiN3cml0ZS50YWJsZSh4LGZpbGU9ImNvdW50bWF0cml4LnRzdiIscXVvdGU9RkFMU0Usc2VwPSJcdCIpCmBgYAoKSW1wb3J0IGZpbGVzIGludG8gcgoKYGBge3J9CmNvbGRhdGEgPC0gcmVhZC50YWJsZSgnc2FtcGxlX2luZm8udHN2JykKUGNnZW5lbmFtZXMgPC0gcmVhZC50YWJsZSgncGNnZW5laWRzLnRzdicsIHJvdy5uYW1lcyA9IDEsIHF1b3RlID0gIiIsIHNlcD0nXHQnLCBmaWxsID0gVFJVRSwgaGVhZGVyID0gVFJVRSkKSVVNODNUYW5qaWxjb3VudG1hdHJpeCA8LSByZWFkLnRhYmxlKCdjb3VudG1hdHJpeC50c3YnKSAKYGBgCgpDbGVhbiBjb3VudG1hdHJpeCBmb3IgUC4gY2lubmFtb21pIGFuYWx5c2lzCgpgYGB7cn0KY29sbGFiZWxzIDwtIGNvbG5hbWVzKElVTTgzVGFuamlsY291bnRtYXRyaXgpIDwtIChjb2xkYXRhJFNhbXBsZSkKY29sbmFtZXMoSVVNODNUYW5qaWxjb3VudG1hdHJpeCkgPC0gY29sbGFiZWxzCgpJVU04M1RhbmppbGNvdW50bWF0cml4IDwtIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKElVTTgzVGFuamlsY291bnRtYXRyaXgsICJnZW5lX2lkIikKCklVTTgzQ291bnRNYXRyaXggPC0gSVVNODNUYW5qaWxjb3VudG1hdHJpeCAlPiUgCiAgZmlsdGVyKHN0cl9kZXRlY3QoZ2VuZV9pZCwgIklVTTgzIikpIAogIApyb3duYW1lcyhJVU04M0NvdW50TWF0cml4KSA8LSBJVU04M0NvdW50TWF0cml4JGdlbmVfaWQKSVVNODNDb3VudE1hdHJpeCA8LSBJVU04M0NvdW50TWF0cml4IFsgLC0xXQpJVU04M0NvdW50TWF0cml4IDwtIGFzLmRhdGEuZnJhbWUoSVVNODNDb3VudE1hdHJpeCkKSVVNODNDb3VudE1hdHJpeCA8LSBJVU04M0NvdW50TWF0cml4WyAsLSgxOjEyKV0KCiN3cml0ZS50YWJsZShJVU04M0NvdW50TWF0cml4LCBmaWxlID0gIlBjX2NvdW50bWF0cml4LnRzdiIscXVvdGU9RkFMU0Usc2VwPSJcdCIpCmBgYAoKQ2xlYW4gY29sZGF0YSBmb3IgRGVzZXEyIG5vcm1hbGlzYXRpb24KCmBgYHtyfQpjb2xEYXRhIDwtIGNvbGRhdGEgJT4lIAogIGZpbHRlcihzdHJfZGV0ZWN0KFNhbXBsZSwgIlBjIikpCgpyb3duYW1lcyhjb2xEYXRhKSA8LSBjb2xEYXRhJFNhbXBsZQpjb2xEYXRhIDwtIGNvbERhdGEgWyAsLTFdIApgYGAKCkRldGVjdCBhbmQgcmVtb3ZlIG91dGxpZXIgZ2VuZXMKCmBgYHtyfQojQ2FsbCBhIGZ1bmN0aW9uIGZyb20gV0dDTkEgcGFja2FnZSB0aGF0IGRldGVjdHMgb3V0bGllcnMgCiNSb3dzIHNob3VsZCBiZSB0aGUgc2FtcGxlcyBhbmQgdGhlIGNvbHVtbnMgZ2VuZXMKZ3NnIDwtIGdvb2RTYW1wbGVzR2VuZXModChJVU04M0NvdW50TWF0cml4KSkKc3VtbWFyeShnc2cpCgojSWYgZmFsc2UsIHRoZW4gdGhlcmUgYXJlIG91dGxpZXJzCmdzZyRhbGxPSyAjRmFsc2UKCnRhYmxlKGdzZyRnb29kR2VuZXMpICMzMDEydG8gYmUgZXhjbHVkZWQKdGFibGUoZ3NnJGdvb2RTYW1wbGVzKSAjQWxsIDEyIHNhbXBsZXMgcGFzc2VkCgojIHJlbW92ZSBnZW5lcyB0aGF0IGFyZSBkZXRlY3RkIGFzIG91dGxpZXJzCmRhdGEgPC0gSVVNODNDb3VudE1hdHJpeFtnc2ckZ29vZEdlbmVzID09IFRSVUUsXQpgYGAKCkRldGVjdCBvdXRsaWVyIHNhbXBsZXMKCmBgYHtyfQojIGRldGVjdCBvdXRsaWVyIHNhbXBsZXMgLSBoaWVyYXJjaGljYWwgY2x1c3RlcmluZwpodHJlZSA8LSBoY2x1c3QoZGlzdCh0KGRhdGEpKSwgbWV0aG9kID0gImF2ZXJhZ2UiKQpncm91cHMgPC0gY3V0cmVlKGh0cmVlLCBrPTIpICMgY3V0IHRyZWUgaW50byBjbHVzdGVycwoKcGxvdChodHJlZSwgbGFiZWxzKGdyb3VwcykpCiMgZHJhdyBkZW5kb2dyYW0gd2l0aCByZWQgYm9yZGVycyBhcm91bmQgdGhlIGNsdXN0ZXJzCnJlY3QuaGNsdXN0KGh0cmVlLCBrPTIsIGJvcmRlcj0icmVkIikKYGBgCgpSZW1vdmUgb3V0bGllciBzYW1wbGVzCgpgYGB7cn0KY2x1c3RlcnMgPC0gYXMuZGF0YS5mcmFtZShncm91cHMpCmhlYWQoY2x1c3RlcnMpCnRhYmxlKGNsdXN0ZXJzKQoKIyB0aGUgYmlnZ2VzdCBjbHVzdGVyIGlzIGNsdXN0ZXIgMiBhbmQgdGhlIHJlc3Qgd2lsbCBiZSB0YWdnZWQgYXMgb3V0bGllcnMKb3V0bGllcnMgPC0gY2x1c3RlcnMgJT4lIGZpbHRlciguLCAhZ3JvdXBzID09IDIpICU+JSByb3cubmFtZXMoKQoKIyBleGNsdWRlIG91dGxpZXIgc2FtcGxlcwpkYXRhLnN1YnNldCA8LSBkYXRhWywhKGNvbG5hbWVzKGRhdGEpICVpbiUgb3V0bGllcnMpXQpgYGAKCiMgTm9ybWFsaXNhdGlvbiB1c2luZyBERVNlcTIgcGFja2FnZQoKVGhlIFdHQ05BIGxpYnJhcnkgcmVxdWlyZXMgbm9ybWFsaXNhdGlvbiB1c2luZyB0aGUgdnN0ICh2YXJpYW5jZS1zdGFiaWxpc2luZyB0cmFuc2Zvcm0pIGZ1bmN0aW9uIGZyb20gdGhlIERFU2VxMiBwYWNrYWdlCgpgYGB7cn0KIyBleGNsdWRlIG91dGxpZXIgc2FtcGxlcyBmcm9tIHRoZSBjb2x1bW4gZGF0YSB0byBtYXRjaCB0aGUgbmV3IGRhdGEKY29sRGF0YSA8LSBjb2xEYXRhICU+JSAKICBmaWx0ZXIoIXJvdy5uYW1lcyguKSAlaW4lIG91dGxpZXJzKQoKIyBtYWtpbmcgc3VyZSB0aGUgcm93bmFtZXMgYW5kIGNvbHVtbiBuYW1lcyBpZGVudGljYWwgZm9yIHRoZSB0d28gZGF0YSBzZXRzCmFsbChyb3duYW1lcyhjb2xEYXRhKSA9PSBjb2xuYW1lcyhkYXRhLnN1YnNldCkpCgojIGNyZWF0ZSBERVNlcSBEYXRhIFNldApkZHMgPC0gREVTZXFEYXRhU2V0RnJvbU1hdHJpeChjb3VudERhdGEgPSBkYXRhLnN1YnNldCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgY29sRGF0YSA9IGNvbERhdGEsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGRlc2lnbiA9IH4gMSkgIyBubyBtb2RlbCwgZm9yIG5vcm1hbGlzYXRpb24gb25seQoKIyByZW1vdmUgYWxsIGdlbmVzIHdpdGggY291bnRzIDwgMTAgaW4gbW9yZSB0aGFuIDkwJSBvZiBzYW1wbGVzICgxMiowLjk9MTAuOCB+IDExKSBhcyBzdWdnZXN0ZWQgYnkgV0dDTkEgb24gUk5Bc2VxIEZBUSAoaHR0cHM6Ly9ob3J2YXRoLmdlbmV0aWNzLnVjbGEuZWR1L2h0bWwvQ29leHByZXNzaW9uTmV0d29yay9ScGFja2FnZXMvV0dDTkEvZmFxLmh0bWwpCiMgPDEwIGluIG1vcmUgdGhhbiA5MCUgc2FtcGxlcwpkZHM5MCA8LSBkZHNbcm93U3Vtcyhjb3VudHMoZGRzKSA+PSAxMCkgPj0gOCxdCm5yb3coZGRzOTApICMgNTQwMyBnZW5lcwoKIyA+PSAxNSkgPj0gOCxdIDQ1MzggZ2VuZXMKIyA+PSAxMCkgPj0gOCxdIDU1NDEgZ2VuZXMgPD09PT09ICNsZXNzIHRoYW4gMTAgY291bnRzIGluICU5MCBvZiBzYW1wbGVzCgoKZ2VuZS5saXN0IDwtIHJvdy5uYW1lcyhkZHM5MCkKI2xhcHBseShnZW5lLmxpc3QsIHdyaXRlLCAiYWxsX2dlbmVzLnR4dCIsIGFwcGVuZD1UUlVFKQoKIyBwZXJmb3JtIHZhcmlhbmNlIHN0YWJpbGl6YXRpb24KZGRzX25vcm0gPC0gdnN0KGRkczkwKQoKIyBnZXQgbm9ybWFsaXplZCBjb3VudHMKbm9ybS5jb3VudHMgPC0gYXNzYXkoZGRzX25vcm0pICU+JSAKICB0KCkKCiNTYXZlIG5vcm1hbGlzZWQgZGF0YXNldCBmb3IgZmFzdGVyIGFuYWx5c2lzIG5leHQgdGltZQpzYXZlUkRTKG5vcm0uY291bnRzLCAibm9ybS5jb3VudHMucmRzIikKYGBgCgojIE5ldHdvcmsgQ29uc3RydWN0aW9uCgpgYGB7cn0KIyBsb2FkIG5vcm0uY291bnRzIG9iamVjdAojbm9ybS5jb3VudHMgPC0gcmVhZFJEUygibm9ybS5jb3VudHMiKQoKIyBDaG9vc2UgYSBzZXQgb2Ygc29mdC10aHJlc2hvbGRpbmcgcG93ZXJzIHRvIGNyZWF0ZSBhIHNjYWxlLWZyZWUgbmV0d29yawpwb3dlciA8LSBjKGMoMToxMCksIHNlcShmcm9tID0gMTIsIHRvID0gMzAsIGJ5ID0gMikpCgojIENhbGwgdGhlIG5ldHdvcmsgdG9wb2xvZ3kgYW5hbHlzaXMgZnVuY3Rpb24Kc2Z0IDwtIHBpY2tTb2Z0VGhyZXNob2xkKG5vcm0uY291bnRzLAogICAgICAgICAgICAgICAgICBwb3dlclZlY3RvciA9IHBvd2VyLAogICAgICAgICAgICAgICAgICBuZXR3b3JrVHlwZSA9ICJzaWduZWQiLAogICAgICAgICAgICAgICAgICBSc3F1YXJlZEN1dCA9IDAuOCwKICAgICAgICAgICAgICAgICAgdmVyYm9zZSA9IDUpCgpzZnQuZGF0YSA8LSBzZnQkZml0SW5kaWNlcwoKYTEgPC0gZ2dwbG90KHNmdC5kYXRhLCBhZXMoUG93ZXIsIFNGVC5SLnNxLCBsYWJlbCA9IFBvd2VyKSkgKwogIGdlb21fcG9pbnQoKSArCiAgZ2VvbV90ZXh0KG51ZGdlX3kgPSAwLjEpICsKICBnZW9tX2hsaW5lKHlpbnRlcmNlcHQgPSAwLjgwLCBjb2xvciA9ICdyZWQnKSArCiAgbGFicyh4ID0gJ1Bvd2VyJywKICAgICAgIHkgPSAnU2NhbGUgZnJlZSB0b3BvbG9neSBtb2RlbCBmaXQsIHNpZ25lZCBSXjInLAogICAgICAgdGl0bGUgPSAnU2NhbGUgaW5kZXBlbmRlbmNlJykgKwogIHRoZW1lX2NsYXNzaWMoKQoKYTIgPC0gZ2dwbG90KHNmdC5kYXRhLCBhZXMoUG93ZXIsIG1lYW4uay4sIGxhYmVsID0gUG93ZXIpKSArCiAgZ2VvbV9wb2ludCgpICsKICBnZW9tX3RleHQobnVkZ2VfeSA9IDAuMSkgKwogIGxhYnMoeCA9ICdQb3dlcicsCiAgICAgICB5ID0gJ01lYW4gQ29ubmVjdGl2aXR5JywKICAgICAgIHRpdGxlID0gJ01lYW4gY29ubmVjdGl2aXR5JykgKwogIHRoZW1lX2NsYXNzaWMoKQogIApncmlkLmFycmFuZ2UoYTEsIGEyLCBucm93ID0gMikgCgpzb2Z0X3Bvd2VyIDwtIHNmdCRwb3dlckVzdGltYXRlICMjIyAxNgoKI3NvZnRfcG93ZXIgPC0gMTYKYGBgCgpDcmVhdGUgbmV0d29yayBhbmQgaWRlbnRpZnkgbW9kdWxlcwpid25ldCA8LSBibG9ja3dpc2VNb2R1bGVzKG5vcm0uY291bnRzLAogICAgICAgICAgICAgICAgIG1heEJsb2NrU2l6ZSA9IDMwMDAwLCAjIHNlbGVjdGVkIHRvdGFsIG51bWJlciBvZiBnZW5lcyBzaW5jZSBDUFUgbWVtb3J5IGlzIHN1ZmZpY2llbnQgKDMyR0Igd29ya3N0YXRpb24gc2hvdWxkIGJlIGFibGUgdG8gaGFuZGxlIHBlcmhhcHMgMzAwMDApCiAgICAgICAgICAgICAgICAgVE9NVHlwZSA9ICJzaWduZWQiLAogICAgICAgICAgICAgICAgIHBvd2VyID0gc29mdF9wb3dlciwKICAgICAgICAgICAgICAgICBtZXJnZUN1dEhlaWdodCA9IDAuMjUsCiAgICAgICAgICAgICAgICAgbnVtZXJpY0xhYmVscyA9IEZBTFNFLCAjIHNldCBhcyBmYWxzZSB0byBhc3NpZ24gY29sb3IgYXMgbGFiZWxzCiAgICAgICAgICAgICAgICAgcmFuZG9tU2VlZCA9IDEyMzQsICMgZm9yIHJlcHJvZHVjaWJpbGl0eSBzaW5jZSB0aGlzIGZ1bmN0aW9uIHVzZXMgY2x1c3RlcmluZwogICAgICAgICAgICAgICAgIHZlcmJvc2UgPSAzKQpOb3RlOiB0aGlzIGNodW5rIGlzIHNldCBub3QgdG8gcnVuIHNpbmNlIHRoZSBibG9ja3dpc2UgTW9kdWxlcyBmdW5jdGlvbiB0YWtlcyBhIGxvbmcgdGltZSB0byBydW4uIFRoZSBSIG9iamVjdCBoYXMgYmVlbiBzYXZlZCBhbmQgaXMgbG9hZGVkIGluIHRoZSBuZXh0IGNodW5rIGZvciBhIGZhc3RlciBydW4gdGltZS4KCmBgYHtyIGV2YWw9RkFMU0UsIGluY2x1ZGU9RkFMU0V9CiMgY29udmVydCBtYXRyaXggdG8gbnVtZXJpYwpub3JtLmNvdW50c1tdIDwtIHNhcHBseShub3JtLmNvdW50cywgYXMubnVtZXJpYykKCiNTdWNjZXNzaXZlbHksIGhpZXJhcmNoaWNhbCBjbHVzdGVyaW5nIHdhcyBwZXJmb3JtZWQgdG8gaWRlbnRpZnkgbW9kdWxlcwp0ZW1wX2NvciA8LSBjb3IKY29yIDwtIFdHQ05BOjpjb3IKCiMgUGFyYW1ldGVycyB0byBiZSB0d2Vha2VkIGxhdGVyCmJ3bmV0IDwtIGJsb2Nrd2lzZU1vZHVsZXMobm9ybS5jb3VudHMsCiAgICAgICAgICAgICAgICAgbWF4QmxvY2tTaXplID0gNzUwMCwgIyBzZWxlY3RlZCB0b3RhbCBudW1iZXIgb2YgZ2VuZXMgc2luY2UgQ1BVIG1lbW9yeSBpcyBzdWZmaWNpZW50ICgzMkdCIHdvcmtzdGF0aW9uIHNob3VsZCBiZSBhYmxlIHRvIGhhbmRsZSBwZXJoYXBzIDMwMDAwKQogICAgICAgICAgICAgICAgIFRPTVR5cGUgPSAic2lnbmVkIiwKICAgICAgICAgICAgICAgICBwb3dlciA9IHNvZnRfcG93ZXIsCiAgICAgICAgICAgICAgICAgbWVyZ2VDdXRIZWlnaHQgPSAwLjIwLAogICAgICAgICAgICAgICAgIHNhdmVUT01zID0gVFJVRSwKICAgICAgICAgICAgICAgICBzYXZlVE9NRmlsZUJhc2UgPSAicGNfVE9NIiwKICAgICAgICAgICAgICAgICBudW1lcmljTGFiZWxzID0gRkFMU0UsICMgc2V0IGFzIGZhbHNlIHRvIGFzc2lnbiBjb2xvciBhcyBsYWJlbHMKICAgICAgICAgICAgICAgICAjcmFuZG9tU2VlZCA9IDEyNDMsICMgZm9yIHJlcHJvZHVjaWJpbGl0eSBzaW5jZSB0aGlzIGZ1bmN0aW9uIHVzZXMgY2x1c3RlcmluZwogICAgICAgICAgICAgICAgIHZlcmJvc2UgPSAzKQoKIyBTYXZlIGJ3bmV0IG9iamVjdApzYXZlUkRTKGJ3bmV0LCAiYnduZXQucmRzIikKCmNvciA8LSB0ZW1wX2NvcgpgYGAKCmBgYHtyfQojIGxvYWQgYnduZXQgb2JqZWN0CmJ3bmV0IDwtIHJlYWRSRFMoImJ3bmV0LnJkcyIpCgptb2R1bGVfZWlnZW5nZW5lcyA8LSBid25ldCRNRXMgJT4lCiAgb3JkZXJNRXMoLikKCmhlYWQobW9kdWxlX2VpZ2VuZ2VuZXMpCgoKIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCB0aGUgbW9kdWxlIGNvbG9ycyBiZWZvcmUgYW5kIGFmdGVyIG1lcmdpbmcgdW5kZXJuZWF0aApwbG90RGVuZHJvQW5kQ29sb3JzKGJ3bmV0JGRlbmRyb2dyYW1zW1sxXV0sIGNiaW5kKGJ3bmV0JHVubWVyZ2VkQ29sb3JzLCBid25ldCRjb2xvcnMpLAogICAgICAgICAgICAgICAgICAgIGMoInVubWVyZ2VkIiwgIm1lcmdlZCIpLAogICAgICAgICAgICAgICAgICAgIGRlbmRyb0xhYmVscyA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgIGFkZEd1aWRlID0gVFJVRSwKICAgICAgICAgICAgICAgICAgICBoYW5nPSAwLjAzLAogICAgICAgICAgICAgICAgICAgIGd1aWRlSGFuZyA9IDAuMDUpCgogIyBncmV5IG1vZHVsZSA9IGFsbCBnZW5lcyB0aGF0IGRvZXNuJ3QgZmFsbCBpbnRvIG90aGVyIG1vZHVsZXMKIAptb2R1bGUudG90YWwgPC0gdGFibGUoYnduZXQkY29sb3JzKQpgYGAKCiMgUmVsYXRlIHRoZSBtb2R1bGVzIHRvIHN0YWdlcwoKYGBge3J9CiMgY3JlYXRlIHRyYWl0cyBmaWxlIC0gYXNzaWduIDEgaWYgYSBzYW1wbGUgaXMgYSBjZXJ0YWluIHN0YWdlLCBlbHNlIGFzc2lnbiAwCiNEaXNfdHJhaXRzIDwtIGNvbERhdGEgJT4lIAojICBtdXRhdGUoRGlzLnZzLmFsbCA9IGlmZWxzZShncmVwbCgnVHJlYXRlZCcsIFRyZWF0bWVudCksIDEsIDApKSAlPiUgCiMgIHNlbGVjdCg0KQoKCmZhY3Rvcl9sZXZlbHMgPC0gdW5pcXVlKGNvbERhdGEkU3RhZ2UpCgojIHRyYW5zZm9ybSBzdGFnZXMgaW50byBmYWN0b3JzIGFuZCBkZWZpbmUgbGV2ZWxzCmNvbERhdGEkU3RhZ2UgPC0gZmFjdG9yKGNvbERhdGEkU3RhZ2UsIGxldmVscyA9IGZhY3Rvcl9sZXZlbHMpCgp0cmFpdHMgPC0gYmluYXJpemVDYXRlZ29yaWNhbENvbHVtbnMoY29sRGF0YSRTdGFnZSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgZHJvcEZpcnN0TGV2ZWxWc0FsbCA9IEZBTFNFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBpbmNsdWRlUGFpcndpc2UgPSBGQUxTRSwKICAgICAgICAgICAgICAgICAgICAgICAgICAgaW5jbHVkZUxldmVsVnNBbGwgPSBUUlVFLAogICAgICAgICAgICAgICAgICAgICAgICAgICBtaW5Db3VudCA9IDEpCgpyb3duYW1lcyh0cmFpdHMpIDwtIGNvbERhdGEkU2FtcGxlCmBgYAoKIyBWaXN1YWxpc2UgbW9kdWxlLXRyYWl0IGFzc29jaWF0aW9uIGFzIGEgaGVhdG1hcAoKYGBge3IgciwgZmlnLndpZHRoPTgsIGZpZy5oZWlnaHQ9MTJ9CiMgRGVmaW5lIG51bWJlcnMgb2YgZ2VuZXMgYW5kIHNhbXBsZXMKblNhbXBsZXMgPC0gbnJvdyhub3JtLmNvdW50cykKbkdlbmVzIDwtIG5jb2wobm9ybS5jb3VudHMpCgojIGNvcnJlbGF0aW9uIGZvciBtb2R1bGUgZWlnZW5nZW5lcyBhbmQgdHJhaXRzCm1vZHVsZS50cmFpdC5jb3JyIDwtIGNvcihtb2R1bGVfZWlnZW5nZW5lcywgdHJhaXRzLCB1c2UgPSAncCcpCm1vZHVsZS50cmFpdC5jb3JyLnB2YWxzIDwtIGNvclB2YWx1ZVN0dWRlbnQobW9kdWxlLnRyYWl0LmNvcnIsIG5TYW1wbGVzKQoKIyBIZWF0IG1hcCB2MiAoZnJvbSBXR0NOQSB0dXRvcmlhbCkKdGV4dE1hdHJpeCA9ICBwYXN0ZShzaWduaWYobW9kdWxlLnRyYWl0LmNvcnIsIDIpLCAiXG4oIiwKICAgICAgICAgICAgICAgICAgICBzaWduaWYobW9kdWxlLnRyYWl0LmNvcnIucHZhbHMsIDEpLCAiKSIsIHNlcCA9ICIiKQpkaW0odGV4dE1hdHJpeCkgPSBkaW0obW9kdWxlLnRyYWl0LmNvcnIpCgoKCnBhcihtYXIgPSBjKDYsIDguNSwgMywgMykpOwpsYWJlbGVkSGVhdG1hcChNYXRyaXggPSBtb2R1bGUudHJhaXQuY29yciwKICAgICAgICAgICAgICAgeExhYmVscyA9IGNvbG5hbWVzKG1vZHVsZS50cmFpdC5jb3JyKSwKICAgICAgICAgICAgICAgeUxhYmVscyA9IHJvd25hbWVzKG1vZHVsZS50cmFpdC5jb3JyKSwKICAgICAgICAgICAgICAgeVN5bWJvbHMgPSByb3duYW1lcyhtb2R1bGUudHJhaXQuY29yciksCiAgICAgICAgICAgICAgIGNvbG9yTGFiZWxzID0gRkFMU0UsCiAgICAgICAgICAgICAgIGNvbG9ycyA9IGJsdWVXaGl0ZVJlZCg1MCksCiAgICAgICAgICAgICAgIHRleHRNYXRyaXggPSB0ZXh0TWF0cml4LAogICAgICAgICAgICAgICB0ZXh0QWRqID0gYygwLjUsIDAuNSksCiAgICAgICAgICAgICAgIHNldFN0ZE1hcmdpbnMgPSBGQUxTRSwKICAgICAgICAgICAgICAgY2V4LmxhYi55ID0gMC42LAogICAgICAgICAgICAgICBjZXgudGV4dCA9IDAuNywKICAgICAgICAgICAgICAgemxpbSA9IGMoLTEsMSksCiAgICAgICAgICAgICAgIG1haW4gPSBwYXN0ZSgiTW9kdWxlLXRyYWl0IHJlbGF0aW9uc2hpcHMiKSkKCgoKIyBnZXQgbnVtYmVyIG9mIGdlbmVzIGZvciBlYWNoIG1vZHVsZQp0YWJsZShid25ldCRjb2xvcnMpCgojVGFnIGdlbmVzIHdpdGggbW9kdWxlIG1lbWJlcnNoaXAgYW5kIHN0b3JlIGl0IGluIGEgdGFibGUKbW9kdWxlLmdlbmUubWFwcGluZyA8LSBhcy5kYXRhLmZyYW1lKGJ3bmV0JGNvbG9ycykKbW9kdWxlLmdlbmUubWFwcGluZwoKYGBgCgoKCgpTaWduaWZpY2FuY2UgaW4gYnJhY2tldHMgc2hvd3MgaG93IHNpZ25pZmljYW50bHkgYXNzb2NpYXRlZCB0aGUgbW9kdWxlIChjbHVzdGVyIG9mIGdlbmVzKSBpcyB0aGUgdHJhaXQgb2YgaW50ZXJlc3QgIApGaW5kIG1vZHVsZXMgdGhhdCBoYXZlIHNpZ25pZmljYW50IGFzc29jaWF0aW9uIHdpdGggZGlzZWFzZSBzdGF0ZQoKQ2FsY3VsYXRlIHRoZSBtb2R1bGUgbWVtYmVyc2hpcCBhbmQgdGhlIGFzc29jaWF0ZWQgcC12YWx1ZXMKVGhlIG1vZHVsZSBtZW1iZXJzaGlwL2ludHJhbW9kdWxhciBjb25uZWN0aXZpdHkgaXMgY2FsY3VsYXRlZCBhcyB0aGUgY29ycmVsYXRpb24gb2YgdGhlIGVpZ2VuZ2VuZSBhbmQgdGhlIGdlbmUgZXhwcmVzc2lvbiBwcm9maWxlLgpUaGlzIHF1YW50aWZpZXMgdGhlIHNpbWlsYXJpdHkgb2YgYWxsIGdlbmVzIG9uIHRoZSBhcnJheSB0byBldmVyeSBtb2R1bGUuCgpVc2luZyB0aGUgZ2VuZSBzaWduaWZpY2FuY2UgeW91IGNhbiBpZGVudGlmeSBnZW5lcyB0aGF0IGhhdmUgYSBoaWdoIHNpZ25pZmljYW5jZSBmb3IgdHJhaXQgb2YgaW50ZXJlc3QgClVzaW5nIHRoZSBtb2R1bGUgbWVtYmVyc2hpcCBtZWFzdXJlcyB5b3UgY2FuIGlkZW50aWZ5IGdlbmVzIHdpdGggaGlnaCBtb2R1bGUgbWVtYmVyc2hpcCBpbiBpbnRlcmVzdGluZyBtb2R1bGVzLgoKIyBHZW5lIFNpZ25pZmljYW5jZSBhdCB0aGUgRWFybHkgUGMtbHVwaW4gaW50ZXJhY3Rpb24KCmBgYHtyfQojIERlZmluZSBhIGdlbmUgc2lnbmlmaWNhbmNlIHZhcmlhYmxlIGZvciBFYXJseQpHUy5FYXJseSA8LSBhcy5udW1lcmljKGNvcihub3JtLmNvdW50cywgdHJhaXRzJGRhdGEuRWFybHkudnMuYWxsLCB1c2UgPSAicCIpKQoKIyBUaGlzIHRyYW5zbGF0ZXMgdGhlIG51bWVyaWMgdmFsdWVzIGludG8gY29sb3JzCkdTLnN0YWdlX2Vhcmx5Q29sb3IgPSBudW1iZXJzMmNvbG9ycyhHUy5FYXJseSwgc2lnbmVkID0gVCkKYmxvY2tudW1iZXIgPSAxCgptb2R1bGVMYWJlbHNBdXRvbWF0aWMgPSBid25ldCRjb2xvcnMKIyBDb252ZXJ0IGxhYmVscyB0byBjb2xvcnMgZm9yIHBsb3R0aW5nCm1vZHVsZUNvbG9yc0F1dG9tYXRpYyA9IGxhYmVsczJjb2xvcnMobW9kdWxlTGFiZWxzQXV0b21hdGljKQoKZGF0Q29sb3JzID0gZGF0YS5mcmFtZShid25ldCRjb2xvcnMsIEdTLnN0YWdlX2Vhcmx5Q29sb3IpW2J3bmV0JGJsb2NrR2VuZXNbW2Jsb2NrbnVtYmVyXV0sIAogICAgXQoKIyBQbG90IHRoZSBkZW5kcm9ncmFtIGFuZCB0aGUgbW9kdWxlIGNvbG9ycyB1bmRlcm5lYXRoCnBsb3REZW5kcm9BbmRDb2xvcnMoYnduZXQkZGVuZHJvZ3JhbXNbW2Jsb2NrbnVtYmVyXV0sIGNvbG9ycyA9IGRhdENvbG9ycywgZ3JvdXBMYWJlbHMgPSBjKCJNb2R1bGUgY29sb3JzIiwgCiAgICAiR1Muc3RhZ2UuRWFybHkiKSwgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLCBhZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUpCmBgYAoKYGBge3J9CmRhdEtNRSA8LSBzaWduZWRLTUUobm9ybS5jb3VudHMsIG1vZHVsZV9laWdlbmdlbmVzKQoKIyBNZWFzdXJlIHRoZSBjb3JyZWxhdGlvbiBvZiB0aGUgZ2VuZSdzIG1vZHVsZSBtZW1iZXJzaGlwIGFuZCB0aGUgYXNzb2NpYXRlZCBwLXZhbHVlcwptb2R1bGUubWVtYmVyc2hpcC5tZWFzdXJlIDwtIGNvcihtb2R1bGVfZWlnZW5nZW5lcywgbm9ybS5jb3VudHMsIHVzZSA9ICdwJykKbW9kdWxlLm1lbWJlcnNoaXAubWVhc3VyZS5wdmFscyA8LSBjb3JQdmFsdWVTdHVkZW50KG1vZHVsZS5tZW1iZXJzaGlwLm1lYXN1cmUsIG5TYW1wbGVzKQoKIyBDYWxjdWxhdGUgdGhlIGdlbmUgc2lnbmlmaWNhbmNlIGFuZCBhc3NvY2lhdGVkIHAtdmFsdWVzCmVhcmx5LmdlbmUuc2lnbmYuY29yciA8LSBjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLkVhcmx5LnZzLmFsbCwgdXNlID0gJ3AnKQplYXJseS5nZW5lLnNpZ25mLmNvcnIucHZhbHMgPC0gY29yUHZhbHVlU3R1ZGVudChlYXJseS5nZW5lLnNpZ25mLmNvcnIsIG5TYW1wbGVzKQoKCmNvbG9yT2ZDb2x1bW4gPSBzdWJzdHJpbmcobmFtZXMoZGF0S01FKSwgNCkKI3NlbGVjdE1vZHVsZXMgPSBjKCJicm93biIsICJtYWdlbnRhIiwgImJsdWUiLCAidHVycXVvaXNlIiwgImxpZ2h0Y3lhbiIpCnNlbGVjdE1vZHVsZXMgPSBjb2xvck9mQ29sdW1uW2NvbG9yT2ZDb2x1bW4gIT0iZ3JleSJdCgojcGFyKG1mcm93ID0gYyg0LCBsZW5ndGgoc2VsZWN0TW9kdWxlcykvNCkpCiAgZm9yIChtb2R1bGUgaW4gc2VsZWN0TW9kdWxlcykgewogICAgICBjb2x1bW4gPSBtYXRjaChtb2R1bGUsIGNvbG9yT2ZDb2x1bW4pCiAgICAgIHJlc3RNb2R1bGUgPSBtb2R1bGVDb2xvcnNBdXRvbWF0aWMgPT0gbW9kdWxlCiAgICAgIHZlcmJvc2VTY2F0dGVycGxvdChkYXRLTUVbcmVzdE1vZHVsZSwgY29sdW1uXSwgR1MuRWFybHlbcmVzdE1vZHVsZV0sIHhsYWIgPSBwYXN0ZSgiTW9kdWxlIE1lbWJlcnNoaXAiLCAKICAgICAgICAgIG1vZHVsZSwgIm1vZHVsZSIpLCB5bGFiID0gIkdTLnN0YWdlX0Vhcmx5IiwgbWFpbiA9IHBhc3RlKCJrTUUuIiwgbW9kdWxlLCAKICAgICAgICAgICJ2cy4gR1MiKSwgY29sID0gbW9kdWxlKQogIH0KYGBgCgpgYGB7ciwgRWFybHllZ2luZW5nZW5lc30KZWFybHlncyA8LSBlYXJseS5nZW5lLnNpZ25mLmNvcnIgJT4lIAogIGFzLmRhdGEuZnJhbWUoKSAlPiUKICB0aWJibGU6OnJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9pZCIpIAoKZWFybHlnc2lkIDwtIGRwbHlyOjpyaWdodF9qb2luKHBjLmFubm90YXRpb25zLCBlYXJseWdzLCBieSA9ICdnZW5lX2lkJykKCmRhdEttZS5pZCA8LSBhcy5kYXRhLmZyYW1lKGRhdEtNRSkgJT4lCiAgcm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX2lkIikgCgplYXJseWVpZ2luZW5nZW5lIDwtIGRwbHlyOjpyaWdodF9qb2luKGVhcmx5Z3NpZCwgZGF0S21lLmlkLCBieSA9ICdnZW5lX2lkJykKYGBgCgojIEdlbmUgU2lnbmlmaWNhbmNlIGF0IHRoZSBNaWQgUGMtbHVwaW4gaW50ZXJhY3Rpb24KCmBgYHtyfQojIERlZmluZSBhIGdlbmUgc2lnbmlmaWNhbmNlIHZhcmlhYmxlIGZvciBFYXJseQpHUy5taWQgPC0gYXMubnVtZXJpYyhjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLk1pZGRsZS52cy5hbGwsIHVzZSA9ICJwIikpCgojIFRoaXMgdHJhbnNsYXRlcyB0aGUgbnVtZXJpYyB2YWx1ZXMgaW50byBjb2xvcnMKR1Muc3RhZ2VfbWlkQ29sb3IgPSBudW1iZXJzMmNvbG9ycyhHUy5taWQsIHNpZ25lZCA9IFQpCmJsb2NrbnVtYmVyID0gMQoKbW9kdWxlTGFiZWxzQXV0b21hdGljID0gYnduZXQkY29sb3JzCiMgQ29udmVydCBsYWJlbHMgdG8gY29sb3JzIGZvciBwbG90dGluZwptb2R1bGVDb2xvcnNBdXRvbWF0aWMgPSBsYWJlbHMyY29sb3JzKG1vZHVsZUxhYmVsc0F1dG9tYXRpYykKCmRhdENvbG9ycyA9IGRhdGEuZnJhbWUoYnduZXQkY29sb3JzLCBHUy5zdGFnZV9taWRDb2xvcilbYnduZXQkYmxvY2tHZW5lc1tbYmxvY2tudW1iZXJdXSwgCiAgICBdCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0gYW5kIHRoZSBtb2R1bGUgY29sb3JzIHVuZGVybmVhdGgKcGxvdERlbmRyb0FuZENvbG9ycyhid25ldCRkZW5kcm9ncmFtc1tbYmxvY2tudW1iZXJdXSwgY29sb3JzID0gZGF0Q29sb3JzLCBncm91cExhYmVscyA9IGMoIk1vZHVsZSBjb2xvcnMiLCAKICAgICJHUy5taWQiKSwgZGVuZHJvTGFiZWxzID0gRkFMU0UsIGhhbmcgPSAwLjAzLCBhZGRHdWlkZSA9IFRSVUUsIGd1aWRlSGFuZyA9IDAuMDUpCmBgYApgYGB7cn0KZGF0S01FIDwtIHNpZ25lZEtNRShub3JtLmNvdW50cywgbW9kdWxlX2VpZ2VuZ2VuZXMpCgojIE1lYXN1cmUgdGhlIGNvcnJlbGF0aW9uIG9mIHRoZSBnZW5lJ3MgbW9kdWxlIG1lbWJlcnNoaXAgYW5kIHRoZSBhc3NvY2lhdGVkIHAtdmFsdWVzCm1vZHVsZS5tZW1iZXJzaGlwLm1lYXN1cmUgPC0gY29yKG1vZHVsZV9laWdlbmdlbmVzLCBub3JtLmNvdW50cywgdXNlID0gJ3AnKQptb2R1bGUubWVtYmVyc2hpcC5tZWFzdXJlLnB2YWxzIDwtIGNvclB2YWx1ZVN0dWRlbnQobW9kdWxlLm1lbWJlcnNoaXAubWVhc3VyZSwgblNhbXBsZXMpCgojIENhbGN1bGF0ZSB0aGUgZ2VuZSBzaWduaWZpY2FuY2UgYW5kIGFzc29jaWF0ZWQgcC12YWx1ZXMKbWlkLmdlbmUuc2lnbmYuY29yciA8LSBjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLk1pZGRsZS52cy5hbGwsIHVzZSA9ICdwJykKbWlkLmdlbmUuc2lnbmYuY29yci5wdmFscyA8LSBjb3JQdmFsdWVTdHVkZW50KG1pZC5nZW5lLnNpZ25mLmNvcnIsIG5TYW1wbGVzKQoKCmNvbG9yT2ZDb2x1bW4gPSBzdWJzdHJpbmcobmFtZXMoZGF0S01FKSwgNCkKI3NlbGVjdE1vZHVsZXMgPSBjKCJyZWQiLCAidGFuIiwgInNhbG1vbiIsImdyZWVuIiwgIm1hZ2VudGEiKQpzZWxlY3RNb2R1bGVzID0gY29sb3JPZkNvbHVtbltjb2xvck9mQ29sdW1uICE9ImdyZXkiXQoKI3BhcihtZnJvdyA9IGMoMywgbGVuZ3RoKHNlbGVjdE1vZHVsZXMpLzMpKQogIGZvciAobW9kdWxlIGluIHNlbGVjdE1vZHVsZXMpIHsKICAgICAgY29sdW1uID0gbWF0Y2gobW9kdWxlLCBjb2xvck9mQ29sdW1uKQogICAgICByZXN0TW9kdWxlID0gbW9kdWxlQ29sb3JzQXV0b21hdGljID09IG1vZHVsZQogICAgICB2ZXJib3NlU2NhdHRlcnBsb3QoZGF0S01FW3Jlc3RNb2R1bGUsIGNvbHVtbl0sIEdTLm1pZFtyZXN0TW9kdWxlXSwgeGxhYiA9IHBhc3RlKCJNb2R1bGUgTWVtYmVyc2hpcCIsIAogICAgICAgICAgbW9kdWxlLCAibW9kdWxlIiksIHlsYWIgPSAiR1Muc3RhZ2VfbWlkIiwgbWFpbiA9IHBhc3RlKCJrTUUuIiwgbW9kdWxlLCAKICAgICAgICAgICJ2cy4gR1MiKSwgY29sID0gbW9kdWxlKQogIH0KYGBgCgpgYGB7cn0KbWlkZ3MgPC0gbWlkLmdlbmUuc2lnbmYuY29yciAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX2lkIikgCgptaWRnc2lkIDwtIGRwbHlyOjpyaWdodF9qb2luKFBjZ2VuZW5hbWVzLCBtaWRncywgYnkgPSAnZ2VuZV9pZCcpCgpkYXRLbWUuaWQgPC0gYXMuZGF0YS5mcmFtZShkYXRLTUUpICU+JQogIHJvd25hbWVzX3RvX2NvbHVtbigiZ2VuZV9pZCIpIAoKbWlkZWlnaW5lbmdlbmUgPC0gZHBseXI6OnJpZ2h0X2pvaW4obWlkZ3NpZCwgZGF0S21lLmlkLCBieSA9ICdnZW5lX2lkJykKYGBgCgojIEdlbmUgU2lnbmlmaWNhbmNlIGF0IHRoZSBMYXRlIFBjLWx1cGluIGludGVyYWN0aW9uCgpgYGB7cn0KIyBEZWZpbmUgYSBnZW5lIHNpZ25pZmljYW5jZSB2YXJpYWJsZSBmb3IgRWFybHkKR1MubGF0ZSA8LSBhcy5udW1lcmljKGNvcihub3JtLmNvdW50cywgdHJhaXRzJGRhdGEuTGF0ZS52cy5hbGwsIHVzZSA9ICJwIikpCgojIFRoaXMgdHJhbnNsYXRlcyB0aGUgbnVtZXJpYyB2YWx1ZXMgaW50byBjb2xvcnMKR1Muc3RhZ2VfbGF0ZUNvbG9yID0gbnVtYmVyczJjb2xvcnMoR1MubGF0ZSwgc2lnbmVkID0gVCkKYmxvY2tudW1iZXIgPSAxCgptb2R1bGVMYWJlbHNBdXRvbWF0aWMgPSBid25ldCRjb2xvcnMKIyBDb252ZXJ0IGxhYmVscyB0byBjb2xvcnMgZm9yIHBsb3R0aW5nCm1vZHVsZUNvbG9yc0F1dG9tYXRpYyA9IGxhYmVsczJjb2xvcnMobW9kdWxlTGFiZWxzQXV0b21hdGljKQoKZGF0Q29sb3JzID0gZGF0YS5mcmFtZShid25ldCRjb2xvcnMsIEdTLnN0YWdlX2xhdGVDb2xvcilbYnduZXQkYmxvY2tHZW5lc1tbYmxvY2tudW1iZXJdXSwgCiAgICBdCgojIFBsb3QgdGhlIGRlbmRyb2dyYW0gYW5kIHRoZSBtb2R1bGUgY29sb3JzIHVuZGVybmVhdGgKcGxvdERlbmRyb0FuZENvbG9ycyhid25ldCRkZW5kcm9ncmFtc1tbYmxvY2tudW1iZXJdXSwgY29sb3JzID0gZGF0Q29sb3JzLCBncm91cExhYmVscyA9IGMoIk1vZHVsZSBjb2xvcnMiLCAKICAgICJHUy5zdGFnZS5MYXRlIiksIGRlbmRyb0xhYmVscyA9IEZBTFNFLCBoYW5nID0gMC4wMywgYWRkR3VpZGUgPSBUUlVFLCBndWlkZUhhbmcgPSAwLjA1KQpgYGAKCmBgYHtyfQpkYXRLTUUgPC0gc2lnbmVkS01FKG5vcm0uY291bnRzLCBtb2R1bGVfZWlnZW5nZW5lcykKCiMgTWVhc3VyZSB0aGUgY29ycmVsYXRpb24gb2YgdGhlIGdlbmUncyBtb2R1bGUgbWVtYmVyc2hpcCBhbmQgdGhlIGFzc29jaWF0ZWQgcC12YWx1ZXMKbW9kdWxlLm1lbWJlcnNoaXAubWVhc3VyZSA8LSBjb3IobW9kdWxlX2VpZ2VuZ2VuZXMsIG5vcm0uY291bnRzLCB1c2UgPSAncCcpCm1vZHVsZS5tZW1iZXJzaGlwLm1lYXN1cmUucHZhbHMgPC0gY29yUHZhbHVlU3R1ZGVudChtb2R1bGUubWVtYmVyc2hpcC5tZWFzdXJlLCBuU2FtcGxlcykKCiMgQ2FsY3VsYXRlIHRoZSBnZW5lIHNpZ25pZmljYW5jZSBhbmQgYXNzb2NpYXRlZCBwLXZhbHVlcwpsYXRlLmdlbmUuc2lnbmYuY29yciA8LSBjb3Iobm9ybS5jb3VudHMsIHRyYWl0cyRkYXRhLkxhdGUudnMuYWxsLCB1c2UgPSAncCcpCmxhdGUuZ2VuZS5zaWduZi5jb3JyLnB2YWxzIDwtIGNvclB2YWx1ZVN0dWRlbnQobGF0ZS5nZW5lLnNpZ25mLmNvcnIsIG5TYW1wbGVzKQoKCmNvbG9yT2ZDb2x1bW4gPSBzdWJzdHJpbmcobmFtZXMoZGF0S01FKSwgNCkKCiNzZWxlY3RNb2R1bGVzID0gYygiZ3JlZW4iLCAidHVycXVvaXNlIiwgImJsdWUiLCAiZ3JleTYwIiwgInNhbG1vbiIpCnNlbGVjdE1vZHVsZXMgPSBjb2xvck9mQ29sdW1uW2NvbG9yT2ZDb2x1bW4gIT0iZ3JleSJdCgojcGFyKG1mcm93ID0gYygzLCBsZW5ndGgoc2VsZWN0TW9kdWxlcykvMykpCiAgZm9yIChtb2R1bGUgaW4gc2VsZWN0TW9kdWxlcykgewogICAgICBjb2x1bW4gPSBtYXRjaChtb2R1bGUsIGNvbG9yT2ZDb2x1bW4pCiAgICAgIHJlc3RNb2R1bGUgPSBtb2R1bGVDb2xvcnNBdXRvbWF0aWMgPT0gbW9kdWxlCiAgICAgIHZlcmJvc2VTY2F0dGVycGxvdChkYXRLTUVbcmVzdE1vZHVsZSwgY29sdW1uXSwgR1MubGF0ZVtyZXN0TW9kdWxlXSwgeGxhYiA9IHBhc3RlKCJNb2R1bGUgTWVtYmVyc2hpcCIsIAogICAgICAgICAgbW9kdWxlLCAibW9kdWxlIiksIHlsYWIgPSAiR1Muc3RhZ2VfTGF0ZSIsIG1haW4gPSBwYXN0ZSgia01FLiIsIG1vZHVsZSwgCiAgICAgICAgICAidnMuIEdTIiksIGNvbCA9IG1vZHVsZSkKICB9CmBgYAp0dXJxdW9pc2UgbW9kdWxlIChIb3J2YXRoIGNhcmVzIG5vdCBmb3IgdGhlIHJlcG9ydGVkIGNvcj0pLiBJbnN0ZWFkLCAKTG9vayBhdCB0aGUgeS1heGlzOiBnZW5lcyB0aGF0IGhhdmUgaGlnaCBwb3NpdGl2ZSBtb2R1bGUgbWVtYmVyc2hpcCB0ZW5kIHRvIGJlIGhpZ2hseSBwb3NpdGl2ZWx5IGNvcnJlbGF0ZWQgd2l0aCB0aGUgbGF0ZSBpbnRlcmFjdGlvbiBvZiBQYyBpbiBsdXBpbiwgCndoZXJlYXMsIGdlbmVzIHdpdGggbmVnYXRpdmUgdmFsdWVzIGluIHRoZSBtb2R1bGUgdGhleSBoYXZlIG5lZ2F0aXZlIHJlbGF0aW9ucyB3aXRoIHRoZSBsYXRlIGludGVyYWN0aW9uIG9mIFBjIGluIGx1cGluLgpDYW4gc2VsZWN0IGVpdGhlcjsgZ2VuZXMgd2l0aCBoaWdoIHBvc2l0aXZlL25lZ2F0aXZlIEdTIG9yIGhpZ2gga01lIChodWIgZ2VuZXMpIAoKYGBge3J9CmxhdGVncyA8LSBsYXRlLmdlbmUuc2lnbmYuY29yci5wdmFscyAlPiUgCiAgYXMuZGF0YS5mcmFtZSgpICU+JQogIHRpYmJsZTo6cm93bmFtZXNfdG9fY29sdW1uKCJnZW5lX2lkIikKCmxhdGVnc2lkIDwtIGRwbHlyOjpyaWdodF9qb2luKFBjZ2VuZW5hbWVzLCBsYXRlZ3MsIGJ5ID0gJ2dlbmVfaWQnKQoKZGF0S21lLmlkIDwtIGFzLmRhdGEuZnJhbWUoZGF0S01FKSAlPiUKICByb3duYW1lc190b19jb2x1bW4oImdlbmVfaWQiKSAKCmxhdGVlaWdpbmVuZ2VuZSA8LSBkcGx5cjo6cmlnaHRfam9pbihsYXRlZ3NpZCwgZGF0S21lLmlkLCBieSA9ICdnZW5lX2lkJykKYGBgCgpgYGB7cn0KIyBjaG9vc2VUb3BIdWJJbkVhY2hNb2R1bGUgcmV0dXJucyB0aGUgZ2VuZSBpbiBlYWNoIG1vZHVsZSB3aXRoIHRoZSBoaWdoZXN0IGNvbm5lY3Rpdml0eSwgbG9va2luZyBhdCBhbGwgZ2VuZXMgaW4gdGhlIGV4cHJlc3Npb24gZmlsZQoKUGNIdWJnZW5lcyA8LSBjaG9vc2VUb3BIdWJJbkVhY2hNb2R1bGUobm9ybS5jb3VudHMsCiAgICAgICAgICAgICAgICAgICAgICAgICBjb2xvcmggPSBtb2R1bGUuZ2VuZS5tYXBwaW5nLAogICAgICAgICAgICAgICAgICAgICAgICAgb21pdENvbG9yID0gImdyZXkiLAogICAgICAgICAgICAgICAgICAgICAgICAgcG93ZXIgPSAgMiwKICAgICAgICAgICAgICAgICAgICAgICAgIHR5cGUgPSAic2lnbmVkIikKUGNIdWJnZW5lcyAKClBjSHViZ2VuZXMuZGYgPC0gUGNIdWJnZW5lcyAlPiUgCiAgYXMuZGF0YS5mcmFtZShyb3cubmFtZXMgPSBOVUxMLCBzdHJpbmdBc0ZhY3RvcnMgPSBGQUxTRSkgCgpQY0h1YmdlbmVzLmRmIDwtIFBjSHViZ2VuZXMuZGYgJT4lIAogICAgICAgcmVuYW1lKCJnZW5lX2lkIiA9ICIuIikKClBjSHViZ2VuZXMuZGYgPC0gdGliYmxlOjpyb3duYW1lc190b19jb2x1bW4oUGNIdWJnZW5lcy5kZiwgIm1vZHVsZSIpCgoKUGNIdWJnZW5lcy5kZiA8LSBkcGx5cjo6cmlnaHRfam9pbihwYy5hbm5vdGF0aW9ucywgUGNIdWJnZW5lcy5kZiwgYnkgPSAnZ2VuZV9pZCcpClBjSHViZ2VuZXMuZGYKYGBgCgpgYGB7cn0KIyBIZWF0bWFwIG9mIG9sZCBtb2R1bGUgZWlnZW4tZ2VuZXMgYW5kIHNhbXBsZXMKI3BkZihmaWxlPSJvbGRNRXMucGRmIixoZWlnaD04MCx3aWR0aD0yMCkKCmxpYnJhcnkoInBoZWF0bWFwIikKCk1FcyA8LSBid25ldCRNRXMKcm93bmFtZXMoTUVzKT1uYW1lcyhub3JtLmNvdW50c1ssOV0pCnBoZWF0bWFwKE1FcyxjbHVzdGVyX2NvbD1ULGNsdXN0ZXJfcm93PVQsc2hvd19yb3duYW1lcz1GLHNob3dfY29sbmFtZXM9VCxmb250c2l6ZT02KQoKCgojIEhlYXRtYXAgb2YgbmV3IG1vZHVsZSBlaWdlbi1nZW5lcyBhbmQgc2FtcGxlIHRyYWl0IChlLmcuIFpvbmUpCmNvbF9hbm4gPC0gY29sRGF0YVssYygxLDQpXQpyb3duYW1lcyhjb2xfYW5uKSA8LSBjb2xfYW5uJFNhbXBsZQpjb2xfYW5uIDwtIGRhdGEuZnJhbWUoY29sX2FubikKY29sX2FubiRTdGFnZSA8LSBhcy5mYWN0b3IoY29sX2FubiRTdGFnZSkKY29sX2FubiA8LSBjb2xfYW5uW29yZGVyKGNvbF9hbm4kU3RhZ2UpLF0KY29sX2FubiRzYW1wbGVfSUQgPC0gTlVMTApoZWFkKGNvbF9hbm4sIDkpCmFubl9jb2xvciA8LSBsaXN0KCJjb2xfYW5uIiA9IGMoIkVhcmx5IiA9ICJ5ZWxsb3ciLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICJNaWRkbGUiID0gImdyZWVuIiwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAiTGF0ZSIgPSAiYmx1ZSIpKQoKZGF0YS5tZSA8LSBkYXRhLmZyYW1lKE1FcykKZGF0YS5tZSA8LSBkYXRhLm1lW29yZGVyKG1hdGNoKHJvd25hbWVzKGRhdGEubWUpLCByb3duYW1lcyhjb2xfYW5uKSkpLF0KZGltKGRhdGEubWUpCgojcGRmKGZpbGU9Im5ld01Fcy5wZGYiLGhlaWdoPTYwLHdpZHRoPTIwKQpyb3duYW1lcyhNRXMpPW5hbWVzKGNvbERhdGFbICwxXSkKcGhlYXRtYXAoZGF0YS5tZSxjbHVzdGVyX2NvbD1ULGNsdXN0ZXJfcm93PUYsc2hvd19yb3duYW1lcz1GLAogICAgICAgICBzaG93X2NvbG5hbWVzPVQsZm9udHNpemU9NiwKICAgICAgICAgYW5ub3RhdGlvbl9yb3cgPSBjb2xfYW5uLCBhbm5vdGF0aW9uX2NvbG9ycyA9IGFubl9jb2xvcikKYGBgCgpgYGB7cixnbXQydGJsfQpsaWJyYXJ5KCJjbHVzdGVyUHJvZmlsZXIiKQpsaWJyYXJ5KCJrYWJsZUV4dHJhIikKCgp3cml0ZUdNVCA8LSBmdW5jdGlvbiAob2JqZWN0LCBmbmFtZSApewogIGlmIChjbGFzcyhvYmplY3QpICE9ICJsaXN0Iikgc3RvcCgib2JqZWN0IHNob3VsZCBiZSBvZiBjbGFzcyAnbGlzdCciKQogIGlmKGZpbGUuZXhpc3RzKGZuYW1lKSkgdW5saW5rKGZuYW1lKQogIGZvciAoaUVsZW1lbnQgaW4gMTpsZW5ndGgob2JqZWN0KSl7CiAgICB3cml0ZS50YWJsZSh0KGMobWFrZS5uYW1lcyhyZXAobmFtZXMob2JqZWN0KVtpRWxlbWVudF0sMikpLG9iamVjdFtbaUVsZW1lbnRdXSkpLAogICAgICAgICAgICAgICAgc2VwPSJcdCIscXVvdGU9RkFMU0UsCiAgICAgICAgICAgICAgICBmaWxlPWZuYW1lLGFwcGVuZD1UUlVFLGNvbC5uYW1lcz1GQUxTRSxyb3cubmFtZXM9RkFMU0UpCiAgfQp9Cgp3cml0ZUdNVChvYmplY3Q9Z3VsLGZuYW1lPSJnb3Rlcm1zLmdtdCIpCmdlbmVzZXRzIDwtIHJlYWQuZ210KCJnb3Rlcm1zLmdtdCIpCgpgYGAKClJ1biBjbHVzdGVycHJvZmlsZXIgdG8gZG8gZW5yaWNobWVudCBvZiBHT3JlZCB3aXRoIGd1bCBnZW5lIHNldHMuCgpgYGB7cixjbHVzdGVycHJvZmlsZXIxfQpiZyA8LSBnZW5lLmxpc3QKCmhlYWQoYmcpCmhlYWQoZ2VuZXNldHMpCgojZWFybHkKCiNncmVlbiA8LSBzdWJzZXQoZWFybHllaWdpbmVuZ2VuZSwgVjEgPiAgMC4yICYgZWFybHllaWdpbmVuZ2VuZSRrTUVncmVlbj4gMC43KQojZ29ncmVlbiA8LSBncmVlbiRnZW5lX2lkCgojY3lhbiA8LSBzdWJzZXQoZWFybHllaWdpbmVuZ2VuZSwgVjEgPiAgMC43ICYgZWFybHllaWdpbmVuZ2VuZSRrTUVjeWFuPiAwLjcpCiNibHVlIDwtIHN1YnNldChlYXJseWVpZ2luZW5nZW5lLCBWMSA+ICAwLjcgJiBlYXJseWVpZ2luZW5nZW5lJGtNRWJsdWU+IDAuNykKI21hZ2VudGEgPC0gc3Vic2V0KGVhcmx5ZWlnaW5lbmdlbmUsIFYxID4gIDAuNyAmIGVhcmx5ZWlnaW5lbmdlbmUka01FbWFnZW50YSA+IDAuNykKI2xpZ2h0Y3lhbiA8LSBzdWJzZXQoZWFybHllaWdpbmVuZ2VuZSwgVjEgPiAgMC43ICYgZWFybHllaWdpbmVuZ2VuZSRrTUVsaWdodGN5YW4gPiAwLjcpCgojZ29jeWFuIDwtIGN5YW4kZ2VuZV9pZAojZ29ibHVlIDwtIGJsdWUkZ2VuZV9pZAojZ29tYWdlbnRhIDwtIG1hZ2VudGEkZ2VuZV9pZAojbGlnaHRjeWFuZ28gPC0gbGlnaHRjeWFuJGdlbmVfaWQgIAogIAoKI21pZAojcmVkIDwtIHN1YnNldChtaWRlaWdpbmVuZ2VuZSwgVjEgPiAgMC4yICYgbWlkZWlnaW5lbmdlbmUka01FcmVkID4gMC43KSAgCiNnb3JlZCA8LSByZWQkZ2VuZV9pZAojdGFuIDwtIHN1YnNldChtaWRlaWdpbmVuZ2VuZSwgVjEgPiAgMC4yICYgbWlkZWlnaW5lbmdlbmUka01FdGFuPiAwLjcpCiNnb3RhbiA8LSB0YW4kZ2VuZV9pZAojZ3JlZW4gPC0gc3Vic2V0KG1pZGVpZ2luZW5nZW5lLCBWMSA+ICAwLjIgJiBtaWRlaWdpbmVuZ2VuZSRrTUVncmVlbj4gMC43KQojZ29ncmVlbiA8LSBncmVlbiRnZW5lX2lkCgoKI2xhdGUKI3R1cnF1b2lzZSA8LSBzdWJzZXQobGF0ZWVpZ2luZW5nZW5lLCBWMSA+ICAwLjcgJiBsYXRlZWlnaW5lbmdlbmUka01FdHVycXVvaXNlID4gMC43KQojZ290dXJxdW9pc2UgPC0gdHVycXVvaXNlJGdlbmVfaWQKI2Jyb3duIDwtIHN1YnNldChsYXRlZWlnaW5lbmdlbmUsIFYxID4gIDAuNSAmIGxhdGVlaWdpbmVuZ2VuZSRrTUVicm93biA+IDAuNSkKI2dvYnJvd24gPC0gYnJvd24kZ2VuZV9pZAojbWlkbmlnaHRibHVlIDwtIHN1YnNldChsYXRlZWlnaW5lbmdlbmUsIFYxID4gIDAuNSAmIGxhdGVlaWdpbmVuZ2VuZSRrTUVtaWRuaWdodGJsdWUgPiAwLjUpCiNnb21pZG5pZ2h0Ymx1ZSA8LSBtaWRuaWdodGJsdWUkZ2VuZV9pZAojbGlnaHRjeWFuCiNtYWdlbnRhCgp5ZWxsb3cgPC0gc3Vic2V0KGxhdGVlaWdpbmVuZ2VuZSwgVjEgPiAgMC4yICYgbGF0ZWVpZ2luZW5nZW5lJGtNRXllbGxvdyA+IDAuNykKZ295ZWxsb3cgPC0geWVsbG93JGdlbmVfaWQKCm9yYV9yZXMgPC0gYXMuZGF0YS5mcmFtZShlbnJpY2hlcihnZW5lID0gZ295ZWxsb3cgLAogIHVuaXZlcnNlID0gYmcsICBtYXhHU1NpemUgPSA1MDAwLCBURVJNMkdFTkUgPSBnZW5lc2V0cywKICBwQWRqdXN0TWV0aG9kPSJmZHIiLCAgcHZhbHVlQ3V0b2ZmID0gMSwgcXZhbHVlQ3V0b2ZmID0gMSAgKSkKCm9yYV9yZXMkZ2VuZUlEIDwtIE5VTEwKb3JhX3JlcyA8LSBzdWJzZXQob3JhX3JlcyxwLmFkanVzdDwwLjA1ICYgQ291bnQgPj01KSAgCm9yYV9yZXNfbmFtZXMgPC0gcm93bmFtZXMob3JhX3JlcykKCmhlYWQob3JhX3JlcykKCmdyIDwtIGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KG9yYV9yZXMkR2VuZVJhdGlvLCIvIiksIltbIiwxKSkgLwogIGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KG9yYV9yZXMkR2VuZVJhdGlvLCIvIiksIltbIiwyKSkKCmJyIDwtIGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KG9yYV9yZXMkQmdSYXRpbywiLyIpLCJbWyIsMSkpIC8KICBhcy5udW1lcmljKHNhcHBseShzdHJzcGxpdChvcmFfcmVzJEJnUmF0aW8sIi8iKSwiW1siLDIpKQoKb3JhX3JlcyRlcyA8LSBnci9icgpvcmFfcmVzIDwtIG9yYV9yZXNbb3JkZXIoLW9yYV9yZXMkZXMpLF0Kb3JhX3JlcyREZXNjcmlwdGlvbj1OVUxMCgpoZWFkKG9yYV9yZXMpICU+JQogIGtibChyb3cubmFtZXMgPSBGQUxTRSwgY2FwdGlvbj0iVG9wIHVwcmVndWxhdGVkIHBhdGh3YXlzIGluIGNsdXN0ZXIiKSAlPiUKICBrYWJsZV9wYXBlcigiaG92ZXIiLCBmdWxsX3dpZHRoID0gRikKCnRvcHVwMiA8LSByZXYoaGVhZChvcmFfcmVzJGVzLDEwKSkKbmFtZXModG9wdXAyKSA8LSByZXYoaGVhZChvcmFfcmVzJElELDEwKSkKCmhlYWQodG9wdXAyKQoKCnBhcihtYXI9Yyg1LDIwLDUsMSkpCgpiYXJwbG90KGModG9wdXAyKSwKICBob3Jpej1UUlVFLGxhcz0xLGNleC5uYW1lcz0wLjY1LAogIG1haW49InRvcCBHTyBlbnJpY2htZW50cyIsCiAgeGxhYj0iRVMiLAogIGNleC5tYWluPTAuOSkKCm10ZXh0KCJPUkEgdGVzdCIpCgpgYGAKUnVuIGNsdXN0ZXJwcm9maWxlciB0byBkbyBlbnJpY2htZW50IG9mIEdPcmVkIHdpdGggZ3VsIGdlbmUgc2V0cy4KCiNgYGB7cixjbHVzdGVycHJvZmlsZXIyfQoKYmcgPC0gZ2VuZS5saXN0CgoKb3JhX3JlcyA8LSBhcy5kYXRhLmZyYW1lKGVucmljaGVyKGdlbmUgPSBnb2dyZWVuICwKICB1bml2ZXJzZSA9IGJnLCAgbWF4R1NTaXplID0gNTAwMCwgVEVSTTJHRU5FID0gZ2VuZXNldHMsCiAgcEFkanVzdE1ldGhvZD0iZmRyIiwgIHB2YWx1ZUN1dG9mZiA9IDEsIHF2YWx1ZUN1dG9mZiA9IDEgICkpCgpvcmFfcmVzJGdlbmVJRCA8LSBOVUxMCm9yYV9yZXMgPC0gc3Vic2V0KG9yYV9yZXMscC5hZGp1c3Q8MC4wNSAmIENvdW50ID49NSkKb3JhX3Jlc19uYW1lcyA8LSByb3duYW1lcyhvcmFfcmVzKQoKaGVhZChvcmFfcmVzKQoKZ3IgPC0gYXMubnVtZXJpYyhzYXBwbHkoc3Ryc3BsaXQob3JhX3JlcyRHZW5lUmF0aW8sIi8iKSwiW1siLDEpKSAvCiAgYXMubnVtZXJpYyhzYXBwbHkoc3Ryc3BsaXQob3JhX3JlcyRHZW5lUmF0aW8sIi8iKSwiW1siLDIpKQoKYnIgPC0gYXMubnVtZXJpYyhzYXBwbHkoc3Ryc3BsaXQob3JhX3JlcyRCZ1JhdGlvLCIvIiksIltbIiwxKSkgLwogIGFzLm51bWVyaWMoc2FwcGx5KHN0cnNwbGl0KG9yYV9yZXMkQmdSYXRpbywiLyIpLCJbWyIsMikpCgpvcmFfcmVzJGVzIDwtIGdyL2JyCm9yYV9yZXMgPC0gb3JhX3Jlc1tvcmRlcigtb3JhX3JlcyRlcyksXQpvcmFfcmVzJERlc2NyaXB0aW9uPU5VTEwKCmhlYWQob3JhX3JlcykgJT4lCiAga2JsKHJvdy5uYW1lcyA9IEZBTFNFLCBjYXB0aW9uPSJUb3AgdXByZWd1bGF0ZWQgcGF0aHdheXMgaW4gY2x1c3RlciIpICU+JQogIGthYmxlX3BhcGVyKCJob3ZlciIsIGZ1bGxfd2lkdGggPSBGKQoKdG9wdXAyIDwtIHJldihoZWFkKG9yYV9yZXMkZXMsMTApKQpuYW1lcyh0b3B1cDIpIDwtIHJldihoZWFkKG9yYV9yZXMkSUQsMTApKQoKaGVhZCh0b3B1cDIpCgoKcGFyKG1hcj1jKDUsMjAsNSwxKSkKCmJhcnBsb3QoYyh0b3B1cDIpLAogIGhvcml6PVRSVUUsbGFzPTEsY2V4Lm5hbWVzPTAuNjUsCiAgbWFpbj0idG9wIEdPIGVucmljaG1lbnRzIiwKICB4bGFiPSJFUyIsCiAgY2V4Lm1haW49MC45KQoKbXRleHQoIk9SQSB0ZXN0IikKCiNgYGAKCiNgYGB7cn0KbW9kdWxlX2RmIDwtIGRhdGEuZnJhbWUoCiAgZ2VuZV9pZCA9IG5hbWVzKGJ3bmV0JGNvbG9ycyksCiAgY29sb3JzID0gbGFiZWxzMmNvbG9ycyhid25ldCRjb2xvcnMpCikKCm1vZHVsZXMub2YuaW50ZXJlc3QgPC0gYygicmVkIiwgInRhbiIsICJncmVlbiIsICJibGFjayIpCgpzdWJtb2QgPC0gbW9kdWxlX2RmICU+JQogIHN1YnNldChjb2xvcnMgJWluJSBtb2R1bGVzLm9mLmludGVyZXN0KQoKcm93Lm5hbWVzKG1vZHVsZV9kZikgPSBtb2R1bGVfZGYkZ2VuZV9pZAoKc3ViZXhwciA9IG5vcm0uY291bnRzICU+JQogIHQoKSAKCnN1YmV4cHIgPSBzdWJleHByW3N1Ym1vZCRnZW5lX2lkLF0KCnN1Ym1vZF9kZiA9IGRhdGEuZnJhbWUoc3ViZXhwcikgJT4lCiAgbXV0YXRlKAogICAgZ2VuZV9pZCA9IHJvdy5uYW1lcyguKQogICkgJT4lCiAgcGl2b3RfbG9uZ2VyKC1nZW5lX2lkKSAlPiUKICBtdXRhdGUoCiAgICBtb2R1bGUgPSBtb2R1bGVfZGZbZ2VuZV9pZCxdJGNvbG9ycwogICkKCnN1Ym1vZF9kZiAlPiUgZ2dwbG90KC4sIGFlcyh4PW5hbWUsIHk9dmFsdWUsIGdyb3VwPWdlbmVfaWQpKSArCiAgZ2VvbV9saW5lKGFlcyhjb2xvciA9IG1vZHVsZSksCiAgICAgICAgICAgIGFscGhhID0gMC4yKSArCiAgdGhlbWVfYncoKSArCiAgdGhlbWUoCiAgICBheGlzLnRleHQueCA9IGVsZW1lbnRfdGV4dChhbmdsZSA9IDkwKQogICkgKwogIGZhY2V0X2dyaWQocm93cyA9IHZhcnMobW9kdWxlKSkgKwogIGxhYnMoeCA9ICJ0cmVhdG1lbnQiLAogICAgICAgeSA9ICJub3JtYWxpemVkIGV4cHJlc3Npb24iKQoKI2BgYAoKIyBFeHBvcnQgdG8gQ3l0b3NjYXBlCgojYGBge3J9CmxvYWQoInBjX1RPTS1ibG9jay4xLlJEYXRhIikKCgpUT00ubWF0ID0gYXMubWF0cml4KFRPTSkKIyBjaG9vc2UgbW9kdWxlCm1vZHVsZSA9ICJjeWFuIgojIGdldCBsaXN0IG9mIGdlbmVzCnByb2JlcyA9IG5hbWVzKGFzLmRhdGEuZnJhbWUobm9ybS5jb3VudHMpKQoKbW9kdWxlQ29sb3JzID0gbGFiZWxzMmNvbG9ycyhid25ldCRjb2xvcnMpCgppbk1vZHVsZSA8LSAobW9kdWxlQ29sb3JzPT1tb2R1bGUpCm1vZFByb2JlcyA9IHByb2Jlc1tpbk1vZHVsZV0KIyBTZWxlY3QgdGhlIGNvcnJlc3BvbmRpbmcgVG9wb2xvZ2ljYWwgT3ZlcmxhcAptb2RUT00gPSBUT00ubWF0W2luTW9kdWxlLCBpbk1vZHVsZV0KCiMgRXhwb3J0IHRoZSBuZXR3b3JrIGludG8gZWRnZSBhbmQgbm9kZSBsaXN0IGZpbGVzIGZvciBDeXRvc2NhcGUKY3l0ID0gZXhwb3J0TmV0d29ya1RvQ3l0b3NjYXBlKG1vZFRPTSwKICBlZGdlRmlsZT1wYXN0ZSgiQ3l0b0VkZ2UiLHBhc3RlKG1vZHVsZSxjb2xsYXBzZT0iLSIpLCIudHh0IixzZXA9IiIpLAogIG5vZGVGaWxlPXBhc3RlKCJDeXRvTm9kZSIscGFzdGUobW9kdWxlLGNvbGxhcHNlPSItIiksIi50eHQiLHNlcD0iIiksCiAgd2VpZ2h0ZWQgPSBUUlVFLCB0aHJlc2hvbGQgPSAwLjAyLG5vZGVOYW1lcz1tb2RQcm9iZXMsCiAgbm9kZUF0dHIgPSBtb2R1bGVDb2xvcnNbaW5Nb2R1bGVdKQojYGBgCgojYGBge3J9CmVkZ2UuYW5ub3RhdGlvbiA8LSBhcy5kYXRhLmZyYW1lKGFwcGx5KHBjLmFubm90YXRpb25zLCAgICAgICAgICAgICAgIyBSZW1vdmUgc3BhY2VzIHdpdGhpbiBwcm90ZWluIElEIAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAyLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmdW5jdGlvbih4KSBnc3ViKCJcXHMrIiwgIiIsIHgpKSkKZWRnZS5hbm5vdGF0aW9uICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIAoKZWRnZSA8LSByZWFkLmRlbGltKCJDeXRvRWRnZWxpZ2h0Z3JlZW4udHh0IikKY29sbmFtZXMoZWRnZSkKY29sbmFtZXMoZWRnZSkgPC0gYygic291cmNlIiwgInRhcmdldCIsIndlaWdodCIsImRpcmVjdGlvbiIsImZyb21BbHROYW1lIiwidG9BbHROYW1lIikKCm5vZGUgPC0gcmVhZC5kZWxpbSgiQ3l0b05vZGVsaWdodGdyZWVuLnR4dCIpCmNvbG5hbWVzKG5vZGUpICAKY29sbmFtZXMobm9kZSkgPC0gYygiZ2VuZV9pZCIsImFsdE5hbWUiLCJub2RlX2F0dHJpYnV0ZXMiKSAKbm9kZUlEIDwtIGRwbHlyOjpyaWdodF9qb2luKGVkZ2UuYW5ub3RhdGlvbiwgbm9kZSwgYnkgPSAnZ2VuZV9pZCcpCm5vZGVJRAoKd3JpdGUuY3N2KG5vZGVJRCwgZmlsZSA9ICdsaWdodGdyZWVubm9kZS50eHQnKQojYGBgCgoKCiMjIyBTZXNzaW9uIEluZm9ybWF0aW9uCgpgYGB7ciBTZXNzaW9uIEluZm8sIGVjaG89RkFMU0V9CnNlc3Npb25JbmZvKCkKYGBgCg==